关于golang的channel死锁分析

死锁常见于有多个协程阻塞,而当主协程中如果有channel发生阻塞,则必然死锁,死锁的直接原因在于channel的读与写没有成对出现,只有读,或者只有写都会造成死锁,特殊情况除外,这个后面会提到。

死锁的发生离不开阻塞,channel的阻塞分两种:

1.对于无缓冲channel,因为其容量为0,数据只读不写,或者只写不读都会造成堵塞

2.对于有缓冲channel,因为设置了容量,在channel容量满之前,仅往里面写数据而不读取是不会导致阻塞的,不会使得协程挂起,但如果channel满了还往里写入数据,就会导致阻塞

下面列举几种死锁的场景

1.只有一个协程(main)时,只往channel读数据或者写数据

package main
import "fmt"
func main() {
	ch1 := make(chan int)
	fmt.Println(<-ch1)
	ch := make(chan int, 3)
	// 在无数据填入ch时,执行fmt.Println(<-ch),会发生死锁
   // fmt.Println(<-ch)
	ch <- 1
	ch <- 2
	ch <- 3

	for v := range ch {
		fmt.Println(v)
		//在读取完ch时,若不关闭ch,继续读取,也同样会发生死锁
		if v==3{
			close(ch)
		}
	}
}

反例来了,将上面的代码放到子协程中,并不会发生死锁,但也不会有任何输出,代码如下,原因是write(ch)在执行 fmt.Println(<-ch)时,因为ch为空,此时write协程被挂起,主协程并没有等待write协程执行完而直接结束,因此并没有死锁

import "fmt"
func main(){
	ch := make(chan int, 3)
	// 在无数据填入ch时,执行fmt.Println(<-ch),会发生死锁
	 fmt.Println(<-ch)
	//ch <- 1
	//ch <- 2
	//ch <- 3
	go write(ch)
	go read(ch)
	//for v := range ch {
	//	fmt.Println(v)
	//	//在读取完ch时,若不关闭ch,继续读取,也同样会发生死锁
	//	if v==3{
	//		close(ch)
	//	}
	//}
}

func write(ch chan int){
	fmt.Println(<-ch)
	ch <- 1
	ch <- 2
	ch <- 3
}

func read(ch chan int){
	for v := range ch {
		fmt.Println(v)
		//在读取完ch时,若不关闭ch,继续读取,也同样会发生死锁
		if v==3{
			close(ch)
		}
	}
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值