简单Go程序运行报死锁,为何case balances <- balance分支未执行?
问题原因说明
你遇到的问题本质是无缓冲通道的发送就绪条件 + goroutine调度时序 + 带default的select非阻塞特性共同导致的:
- 你声明的
balances是无缓冲通道,无缓冲通道的发送操作要成功,必须同时有其他goroutine在该通道上等待接收值 - 带
default分支的select不会阻塞等待case就绪,会立刻判断当前所有case是否满足执行条件,不满足就直接走default - 执行到
select代码块时,你启动的匿名子goroutine大概率还没被调度到<-balances这行代码,此时没有接收方在等待balances的发送,所以case balances <- balance:触发条件不满足,直接走了default分支
死锁的产生逻辑
走default分支之后,两个goroutine进入循环等待状态:
- 主goroutine卡在
<-done,等待子goroutine往done通道写入值 - 子goroutine卡在
<-balances,等待主goroutine往balances通道写入值
所有goroutine都处于阻塞状态,Go runtime就会抛出死锁错误。
修复方案
你可以根据场景选任意一种方案修复:
- 方案1:去掉
select的default分支,让select阻塞等待通道就绪,等子goroutine调度到接收操作后,发送case会自动触发
// 修改select部分代码 select { case balances <- balance: fmt.Println("done case") }
- 方案2:初始化
balances时设置为长度1的带缓冲通道,就算接收方未就绪,发送方也可以先把值写入缓冲区,不会进入default分支
// 修改balances初始化代码 balances := make(chan int, 1)
- 方案3:在
select执行前添加短时间休眠,等待子goroutine调度到接收操作再执行select(仅用于理解调度时序,不推荐生产环境使用)
// 需先导入time包,在select前加如下代码 time.Sleep(time.Millisecond)
内容的提问来源于stack exchange,提问作者bfforever
相关产品推荐
相关产品推荐

