Go语言中使用带channel的select语句时程序为何阻塞?
问题根因
- 通道初始化顺序错误:你在启动goroutine
go a.f()之后才对a.exit做初始化操作,如果goroutine调度速度快于main的初始化逻辑,select判断时a.exit还是nil通道,读取nil通道会永远阻塞,程序直接死锁,甚至连-----over-----都无法输出,当前能输出是调度顺序巧合,逻辑不稳定。 - 无缓冲通道双向写入无接收方导致阻塞:
a.exit是无缓冲通道,main向通道写入true后,f的goroutine成功读取到信号输出-----over-----,接下来你在f中再次向a.exit写入true,此时没有任何其他goroutine在监听该通道的读取操作,无缓冲通道的写入操作会永久阻塞,因此无法执行后续的打印语句。
修复方案
如果需要实现「main发退出信号 + 子goroutine返回退出确认」的逻辑,可以提前初始化带缓冲的通道,同时main等待确认信号即可,修复后的代码如下:
package main import ( "fmt" ) type A struct{ exit chan bool } func (a *A) f(){ select{ // 业务逻辑的case写在此处 case <- a.exit: fmt.Println("-----over-----") // 发送退出确认 a.exit <- true fmt.Println("+++++over++++++") } } func main() { a := A{ // 初始化带1个缓冲的通道,避免写入时无接收方阻塞 exit: make(chan bool, 1), } // 通道初始化完成后再启动goroutine,避免读取nil通道 go a.f() // 发送退出信号 a.exit <- true // 等待子goroutine的退出确认 <-a.exit }
如果是多goroutine的退出通知场景,更推荐直接使用标准库的context.Context实现,支持级联取消、超时控制,不需要手动维护多个通道。
内容的提问来源于stack exchange,提问作者RITY
相关产品推荐
相关产品推荐

