Go中缓冲通道的死锁
Posted
技术标签:
【中文标题】Go中缓冲通道的死锁【英文标题】:Deadlocks with buffered channels in Go 【发布时间】:2021-12-25 06:25:13 【问题描述】:我遇到以下代码fatal error: all goroutines are asleep - deadlock!
我使用缓冲通道是否正确?如果您能给我指点,我将不胜感激。不幸的是,我已经走投无路了。
func main()
valueChannel := make(chan int, 2)
defer close(valueChannel)
var wg sync.WaitGroup
for i := 0; i < 10; i++
wg.Add(1)
go doNothing(&wg, valueChannel)
for
v, ok := <- valueChannel
if !ok
break
fmt.Println(v)
wg.Wait()
func doNothing(wg *sync.WaitGroup, numChan chan int)
defer wg.Done()
time.Sleep(time.Duration(rand.Intn(1000)) * time.Millisecond)
numChan <- 12
【问题讨论】:
【参考方案1】:收到所有值后,<- valueChannel
上的主要 goroutine 阻塞。关闭通道以解除对主 goroutine 的阻塞。
func main()
valueChannel := make(chan int, 2)
var wg sync.WaitGroup
for i := 0; i < 10; i++
wg.Add(1)
go doNothing(&wg, valueChannel)
// Close channel after goroutines complete.
go func()
wg.Wait()
close(valueChannel)
()
// Receive values until channel is closed.
// The for / range loop here does the same
// thing as the for loop in the question.
for v := range valueChannel
fmt.Println(v)
Run the example on the playground.
上面的代码独立于 goroutines 发送的值的数量。
如果 main()
函数可以确定 goroutines 发送的值的数量,则从 main()
接收该数量的值:
func main()
const n = 10
valueChannel := make(chan int, 2)
for i := 0; i < n; i++
go doNothing(valueChannel)
// Each call to doNothing sends one value. Receive
// one value for each call to doNothing.
for i := 0; i < n; i++
fmt.Println(<-valueChannel)
func doNothing(numChan chan int)
time.Sleep(time.Duration(rand.Intn(1000)) * time.Millisecond)
numChan <- 12
Run the example on the playground.
【讨论】:
非常感谢。以上工作。但是,为什么 ``` wg.Wait() 和 close(valueChannel) ``` 应该在 go 例程中?我试图将它们放在 go 例程之外,但失败了 :( 要中断接收循环,对close(valueChannel)
的调用必须与接收循环同时执行。 Goroutines 用于并发执行代码。
main函数作为Goroutine运行,你可以直接把close放在main里面。 @CeriseLimón
@CeriseLimón,那为什么我的代码对我的答案有效?
第3行有:defer close(valueChannel)
【参考方案2】:
主要问题在频道接收的for循环上。 逗号 ok 习语在通道上略有不同,ok 表示接收到的值是在通道上发送的(真)还是因为通道关闭且为空而返回的零值(假)。 在这种情况下,通道正在等待发送数据,因为它已经完成了十次发送值:死锁。 因此,除了代码设计之外,如果我只需要在此处进行尽可能少的更改,那就是:
func main()
valueChannel := make(chan int, 2)
defer close(valueChannel)
var wg sync.WaitGroup
for i := 0; i < 10; i++
wg.Add(1)
go doNothing(&wg, valueChannel)
for i := 0; i < 10; i++
v := <- valueChannel
fmt.Println(v)
wg.Wait()
func doNothing(wg *sync.WaitGroup, numChan chan int)
defer wg.Done()
time.Sleep(time.Duration(rand.Intn(1000)) * time.Millisecond)
numChan <- 12
【讨论】:
以上是关于Go中缓冲通道的死锁的主要内容,如果未能解决你的问题,请参考以下文章