为什么这段Go语言交替打印代码会引发死锁?
Go交替打印代码的死锁原因分析
原死锁代码
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的生命周期:它负责打印1、3、...、9,循环条件是
i < 10,打印完9后循环直接结束,执行defer close(evenCh)就退出了,后续不会再接收oddCh的任何消息。 - 偶数goroutine的阻塞点:当i=10时,原代码先执行
oddCh <- struct{}{}——由于oddCh是无缓冲通道,发送操作必须等到有接收者才能完成,但此时奇数goroutine已经退出,没有任何goroutine会接收这个发送的值,导致偶数goroutine卡在这个发送步骤上。 - 连锁阻塞触发死锁:偶数goroutine卡死后,无法执行后续的
close(oddCh)和close(done),而main goroutine一直在等待<-done的信号,所有goroutine都进入阻塞状态,最终触发死锁。
交换顺序后的修复逻辑
将oddCh <- struct{}{}和i=10的判断代码交换顺序后,偶数goroutine的逻辑变为:
go func() { defer close(done) for i := 2; i <= 10; i += 2 { <-evenCh fmt.Println(i) if i == 10 { close(oddCh) break } oddCh <- struct{}{} } }()
此时:
- 处理i=10时,打印完成后直接关闭
oddCh并跳出循环,不会执行oddCh <- struct{}{}的发送操作,避免了无接收者的阻塞问题。 - 偶数goroutine退出前执行
defer close(done),main goroutine从已关闭的done通道接收会立即返回,程序正常结束。
内容的提问来源于stack exchange,提问作者crazyfrank
相关产品推荐
相关产品推荐

