这是一段使用Go的通道交替打印1到10的代码,但是这样写会导致死锁。
func main() {
oddCh := make(chan struct{})
evenCh := make(chan struct{})
done := make(chan struct{})
go func() {
defer close(evenCh)
for i := 1; i < 10; i += 2 {
<-oddCh
fmt.Println(i)
evenCh <- struct{}{}
}
}()
go func() {
defer close(done)
for i := 2; i <= 10; i += 2 {
<-evenCh
fmt.Println(i)
oddCh <- struct{}{}
if i == 10 {
close(oddCh)
break
}
}
}()
oddCh <- struct{}{}
<-done
}
输出:
1
2
3
4
5
6
7
8
9
10
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [chan receive]:
main.main()
D:/Gocode/alternateprint/main.go:34 +0x125
goroutine 7 [chan send]:
main.main.func2()
D:/Gocode/alternateprint/main.go:24 +0xcb
created by main.main in goroutine 1
D:/Gocode/alternateprint/main.go:19 +0x105
我认为第一个goroutine执行到i=9后唤醒第二个goroutine,然后第二个goroutine执行到i=10并打印10,向oddCh写入信号,然后关闭oddCh。这不是一个大问题,但它确实会导致死锁。然后我只是交换了两段的顺序:
if i == 10 {
close(oddCh)
break
}
和
oddCh <- struct{}{}
交换后没有死锁,输出正常。
没有与事件循环中的最终
oddCh <- struct{}{}
相对应的读取。在全局范围内,有 6 次写入oddCh
,而只有 5 次读取,因此最终的写入必须永远阻塞。你的程序等待 done
,第二个(偶数)goroutine 负责在退出时关闭该通道,因此阻塞第二个 goroutine 会导致你的程序死锁。
如果您将
if i == 10 { close(oddCh); break }
放在该代码中,那么 oddCh
将关闭(不会阻塞)并且 goroutine 退出。