Golang中WaitGroup与Channel结合使用的程序故障排查
代码问题分析
1. WaitGroup 计数不匹配
你启动了3个popMessage goroutine,但仅调用wg.Add(2),这会引发核心问题:
- 当第二个goroutine因channel阻塞无法执行
wg.Done()时,wg.Wait()会一直等待剩余计数完成,直接导致程序死锁。 - 若后续goroutine全部执行完成,
wg.Done()被调用3次,超出Add的计数,还会触发WaitGroup内部panic。
2. Channel 阻塞引发的连锁死锁
你创建的channel c缓冲容量仅为1:
- 第一个goroutine执行
c <- *i时,缓冲有空位,顺利完成解锁并调用wg.Done(); - 第二个goroutine加锁后执行
c <- *i,此时缓冲已被占满,会阻塞在发送操作上,且互斥量未解锁; - 第三个goroutine因等待互斥量解锁,全程无法推进;
- 最终
wg剩余计数未完成,wg.Wait()无限等待,程序彻底卡死。
3. 未关闭 Channel 导致后续阻塞
即便修复前两个问题,最后的for x := range c循环也会一直阻塞——range遍历channel时,只有当channel被关闭且所有数据读取完成后才会退出,而你的代码从未关闭c。
修复建议
- 对齐
wg.Add()数量与goroutine数量:这里应改为wg.Add(3); - 在所有goroutine完成后关闭channel,让
range循环能正常退出:wg.Wait() close(c) // 关闭channel,终止range遍历 fmt.Printf("final %d\n", *intPtr) for x := range c { fmt.Printf("channel %d\n", x) } - 可将channel发送操作移到互斥锁外,避免锁被长时间占用:
func popMessage(wg *sync.WaitGroup, m *sync.Mutex, i *int, c chan<- int) { m.Lock() *i++ val := *i m.Unlock() // 先解锁再发送,减少锁持有时间 c <- val fmt.Printf("pop %d\n", val) wg.Done() }
内容的提问来源于stack exchange,提问作者Kendall Chenoweth
相关产品推荐
相关产品推荐

