WaitGroup.Add()必须在启动goroutine前调用,且参数>0;不可在goroutine中调用Add();defer wg.Done()不能漏括号;必须传指针避免复制;不解决数据竞争,共享变量需额外同步。

WaitGroup 必须在启动 goroutine 前调用 Add()
常见错误是先开 goroutine,再在 goroutine 里调用 WaitGroup.Add(1) —— 这会导致 Wait() 可能永远阻塞,或 panic:「negative WaitGroup counter」。因为 Add() 和 Done() 不是原子配对操作,必须由主线程提前声明任务数。
-
Add()必须在go语句之前调用,且参数 > 0 - 若任务数动态不确定(比如从 channel 收集),需先遍历一次计数,或改用
sync.Map+ 计数器 - 传入 0 给
Add(0)合法但无意义;传负数直接 panic
别在 goroutine 里 defer WaitGroup.Done() 而不加括号
写成 defer wg.Done(漏掉括号)是静默错误:函数未执行,Done() 被当值传递,最终计数器不减,Wait() 永远不返回。Go 编译器不会报错,运行时行为完全异常。
go func() {
defer wg.Done() // ✅ 正确:带括号,实际调用
// ... work
}()
go func() {
defer wg.Done // ❌ 错误:只是取函数地址,不调用
// ... work
}()
WaitGroup 不能被复制,跨 goroutine 传递必须用指针
sync.WaitGroup 包含 mutex 等非可复制字段,值拷贝会破坏内部状态。常见于:将 wg 作为参数传给函数却用了值传递、在循环中创建临时 wg 变量。
- 函数参数类型必须是
*sync.WaitGroup,不是sync.WaitGroup - 切片或 map 中存 wg 也必须存指针:
[]*sync.WaitGroup - 初始化后不要赋值给另一个变量(如
wg2 := wg),否则后续Done()作用于副本,主 wg 无变化
WaitGroup 不解决数据竞争,共享变量仍需额外同步
WaitGroup 只保证 goroutine 执行完毕的时机,不保护读写共享内存。例如多个 goroutine 向同一 slice 追加元素,即使用了 WaitGroup,仍可能 panic 或丢数据。
立即学习“go语言免费学习笔记(深入)”;
- 写共享变量前,考虑
sync.Mutex、sync.RWMutex或channels - 优先用 channel 传递结果,而非让 goroutine 直接修改外部变量
- 简单累加可用
sync/atomic(如atomic.AddInt64),比锁更轻量
WaitGroup 的真正难点不在语法,而在于它只回答「都做完了吗」,从不回答「做对了吗」——共享状态的正确性,得靠你亲手守住。










