Goroutines用time.Sleep正常,WaitGroup触发死锁?同步问题解析
为什么用sync.WaitGroup等待交替Goroutine会触发死锁,而time.Sleep不会?
死锁的根本原因(WaitGroup版本)
在无缓冲channel的场景下,两个goroutine的最后一次发送操作会引发阻塞链:
f1完成第4次循环后,向ch2发送数据并调用wg.Done(),随后退出。f2完成第4次循环处理后,尝试向ch1发送数据,但此时ch1是无缓冲channel,且没有任何goroutine(主goroutine在wg.Wait()阻塞,f1已退出)准备接收这个值,导致f2阻塞在ch1 <- num*i,无法执行wg.Done()。- 主goroutine一直等待两个
wg.Done()调用,而f2永远无法完成任务,最终所有goroutine都进入阻塞状态,触发死锁。
time.Sleep与WaitGroup的行为差异
time.Sleep的工作逻辑
time.Sleep仅让主goroutine暂停指定时长,不关心其他goroutine是否执行完成。- 当睡眠时间结束,主程序直接退出,Go运行时会强制终止所有未完成的goroutine。即使此时
f2可能还阻塞在发送操作,程序也不会触发死锁检测(因为进程已经退出)。 - 你提供的time.Sleep版本额外使用了缓冲channel(大小1),这让最后一次发送操作不会阻塞,两个goroutine都能正常完成循环,程序自然无错误。
WaitGroup的工作逻辑
sync.WaitGroup的核心是等待所有注册的goroutine明确完成任务(即调用wg.Done())。- 主goroutine会一直阻塞在
wg.Wait(),直到所有wg.Add()注册的计数被wg.Done()清零。 - 如果任何一个goroutine因为channel阻塞无法调用
wg.Done(),主goroutine会永远等待,最终触发死锁。
缓冲channel为什么能解决问题
当channel带有缓冲时,发送操作只要缓冲未被填满就不会阻塞:
f2最后一次向ch1发送数据时,缓冲channel可以容纳这个值,无需等待接收方,f2能顺利完成循环并调用wg.Done()。- 主goroutine收到两个
wg.Done()后,wg.Wait()结束,程序正常退出,不会出现死锁。
内容的提问来源于stack exchange,提问作者Shubham Chadokar
相关产品推荐
相关产品推荐

