Go语言Worker Pool添加WaitGroup后死锁的核心原因分析
分析Worker Pool死锁的核心原因
嗨,我来帮你拆解这个死锁问题的本质,其实问题就出在通道关闭时机和WaitGroup等待逻辑的配合上:
死锁的触发链条
- Worker的循环特性:你启动的每个worker goroutine都在执行
for job := range JobChan,这个循环的规则是只有当通道被关闭且所有元素都被接收完毕时,才会退出循环。只要通道没关,worker就会一直阻塞在接收操作上,永远不会执行defer wg1.Done()(因为函数还没走到退出逻辑)。 - Main goroutine的阻塞点:当你把10000个任务全部发送到
JobChan后,直接调用了wg.Wait(),此时main goroutine会阻塞在这里,等待所有worker调用Done()来完成WaitGroup的计数。 - 互相等待的死锁:一边是worker们盯着
JobChan等新任务(永远等不到,因为任务已经发完了),不肯退出;另一边是main goroutine等着worker的Done()信号,不肯继续。双方都陷入了无限等待,最终触发死锁报错。
为什么不使用WaitGroup时程序“正常”?
其实那不是真的正常——当你去掉WaitGroup后,main goroutine在发送完所有任务后就直接退出了,Go程序会强制终止所有未完成的goroutine,所以worker还没来得及因为通道未关闭而阻塞,就被结束了。你看不到死锁,但这并不是正确的优雅退出逻辑。
修复方案
解决方法很简单:在所有任务发送完成后,关闭JobChan,让worker明确知道不会再有新任务进来,循环可以正常退出。修改后的关键代码片段如下:
// Create jobs pool for _, job := range jobsArr { JobChan <- job } close(JobChan) // 取消注释,移到这个位置! wg.Wait()
这样worker在接收完所有任务后,会因为通道关闭而退出循环,执行defer wg1.Done(),WaitGroup的计数归0后,main goroutine就能正常结束了。
内容的提问来源于stack exchange,提问作者Sammi Kerra
相关产品推荐
相关产品推荐

