关于调用两次 <-done死锁的问题
来源:11-2 使用Channel等待任务结束
Yao_Jerry
2020-12-27

chanDemo()里这个方法一共写了4个for循环,chanDemo属于main这个协程的。
第一个循环里面创建了10个Worker和10个子协程,可以简单粗暴的理解为一个worker对应着一个协程。
第二个循环里面分别往10个Worker的 in 写入小写的字母,在写入了数据之后,不同Worker对应的协程就分别取出数据并打印出来,然后 done <- true,陷入阻塞的状态,等待其它的协程来读取这个数据并将它唤醒,注意,在第二循环执行完毕的时候,我们可以简单的认为10个协程都陷入了阻塞。
第三个循环继续往不同的Worker的 in 里写入大写的字母,注意,Worker的in是一个无缓冲的管道,被写入或读取数据时都会陷入阻塞,关键点就在与,done <- true,陷入阻塞的状态,而我们是在main协程里面去读取这个done的,但是在读取之前,main协程已经被Worker[i].in <- ‘A’ + i 这句代码给阻塞住了,那main协程与子协程都在互相等待对方来读取,所以就陷入了死锁。。。。
这个问题在一开始看视频的时候没懂,自己再想了半天还是没明白,再次看视频的时候才想明白,在这里记录一下
5回答
-
谢谢同学的详细分析。的确是这样的,分析的非常到位。
012020-12-30 -
慕斯卡7374230
2022-03-12
第一个循环里面创建了20个channel(10个in 和10个done)和10个子协程
第二个循环执行过后,10个in通道并没有阻塞,因为已经被读取了。同时另外的10个done却堆满了true,正在等待其他协程(也就是main所在的协程)读取,此时10个done被阻塞着。
第三个循环在执行时,又往这10个in里扔进了大写字母,并且被顺利的读出,此时并没有发生阻塞。但是此时上一个循环填充的10个done还处在阻塞状态,这个时候再往done里扔消息,就会造成done通道的阻塞。
如果不相信上面的结论,可以尝试一下这个:
func createWorker(i int) worker {
w := worker{
in: make(chan int),
done: make(chan bool, 2),
}
go doWorker(i, w)
return w
}
这样的话,程序就不会出问题了。
但是老师的课程中,居然又新开了一个goroutine
go func() { w.done <- true }()
这依然是有done 阻塞的问题,新开groutine只是让w.done <-true这件事跟main并行而已。
00 -
慕仙908892
2021-04-11
再比如 我循环3遍 这里缓冲改成2
package main
import "fmt"
func makeWork(i int, w Work){
for {
fmt.Printf("%d 号 chan revice %c \n", i, <-w.In)
w.Done <- true
}
}
type Work struct {
In chan int
Done chan bool
}
func createIntWork(i int) Work{
w := Work{}
w.In = make(chan int)
w.Done = make(chan bool, 2)
go makeWork(i, w)
return w
}
func main() {
var woker [1000]Work
for i := 0; i < 1000; i++ {
woker[i] = createIntWork(i)
}
for i, w := range woker {
w.In <- 'a' + i
}
for i, w := range woker {
w.In <- 'A' + i
}
for i, w := range woker {
w.In <- 'a' + i
}
for _, w := range woker {
<-w.Done
<-w.Done
<-w.Done
}
}你要不再来解析一下~ 嘿嘿00 -
慕仙908892
2021-04-11
你这样只通过表象来分析问题是没用的~
012022-02-14 -
慕仙908892
2021-04-11
package main
import "fmt"
func makeWork(i int, w Work){
for {
fmt.Printf("%d 号 chan revice %c \n", i, <-w.In)
w.Done <- true
}
}
type Work struct {
In chan int
Done chan bool
}
func createIntWork(i int) Work{
w := Work{}
w.In = make(chan int)
w.Done = make(chan bool, 1) //只要给他定一个buff就不会阻塞了~
go makeWork(i, w)
return w
}
func main() {
var woker [10]Work
for i := 0; i < 10; i++ {
woker[i] = createIntWork(i)
}
for i, w := range woker {
w.In <- 'a' + i
}
for i, w := range woker {
w.In <- 'A' + i
}
for _, w := range woker {
<-w.Done
<-w.Done
}
}00
相似问题