Go中range遍历channel出现死锁,是否必须在写完数据后关闭channel?
Go Channel死锁问题原因解析
死锁触发原因
Go语言中使用for x := range ch语法遍历channel时,遍历逻辑会持续阻塞等待接收新数据,只有当channel被显式关闭后,遍历循环才会正常退出。
对应你提供的代码场景:
- 写入goroutine循环往
ch1写入10条数据后直接退出,没有关闭channel - 读取goroutine遍历完10条数据后,仍然会持续等待
ch1的新数据,陷入永久阻塞 - 主goroutine阻塞在
wg.Wait()调用,等待读取goroutine执行wg.Done()回调
此时所有goroutine都处于阻塞挂起状态,没有可运行的goroutine,Go runtime就会抛出死锁错误。
当你取消注释关闭channel的代码后,写入goroutine退出前会自动关闭ch1,读取goroutine遍历完所有数据后检测到channel已关闭,就会正常退出循环,执行wg.Done(),主goroutine的等待逻辑也会正常返回,程序顺利结束。
写完数据是否必须手动关闭channel?
不一定,分场景判断:
- 如果你后续的接收逻辑使用
for range遍历channel,或者依赖v, ok := <-ch的ok返回值判断数据是否接收完毕,必须手动关闭channel,否则会出现接收方永久阻塞的问题,甚至触发死锁 - 如果你的channel生命周期和程序运行周期一致、接收方有其他独立的退出判断逻辑、或者可以保证不会出现接收方无限等待的情况,不需要手动关闭channel。未关闭的channel只要没有外部引用,Go GC会自动回收相关资源,不会出现资源泄漏
另外需要注意:已经关闭的channel不能再写入数据,也不能重复关闭,否则会直接触发panic。
内容的提问来源于stack exchange,提问作者Kade Wu
相关产品推荐
相关产品推荐

