关于调用两次 <-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回答

ccmouse

2020-12-29

谢谢同学的详细分析。的确是这样的,分析的非常到位。

0
1
Yao_Jerry
非常感谢!
2020-12-30
共1条回复

慕斯卡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并行而已。



0
0

慕仙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
  }

}你要不再来解析一下~ 嘿嘿

0
0

慕仙908892

2021-04-11

你这样只通过表象来分析问题是没用的~ 

0
1
尼克2018
过分了啊,他只是记录自己理解啊。
2022-02-14
共1条回复

慕仙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
  }

}

0
0

Google资深工程师深度讲解Go语言 由浅入深掌握Go语言

语法+分布式爬虫实战 为转型工程师量身打造

5995 学习 · 1909 问题

查看课程