sync.waitgroup 是轻量不可复制的同步原语,add 必须在 goroutine 启动前调用,done 应 defer 调用,可重用但禁止 add 与 wait 并发,零值安全但严禁复制。

sync.WaitGroup 不是“教程工具”,它是一个轻量、不可复制、需谨慎调用的同步原语——用错一次就 panic,写对了才真正省事。
WaitGroup.Add 必须在 goroutine 启动前调用
这是最常踩的坑:把 wg.Add(1) 放在 go 语句之后,或塞进 goroutine 里,会导致 Wait() 永远不返回,或 panic “negative WaitGroup counter”。
-
Add的作用是预登记“待等待任务数”,必须在go启动前完成 - 闭包中若用循环变量(如
for i := range urls),要传参避免所有 goroutine 共享同一个i值 - 如果任务数动态不确定(比如从 channel 拉取),得先 collect 再
Add(len(tasks)),不能边拉边Add
Done 要确保执行,推荐 defer wg.Done()
Done() 是计数器减 1,漏调 = 计数器卡住 = Wait() 死锁。用 defer 是最稳妥的方式,但要注意:
- 必须在 goroutine 内部调用,不能由主 goroutine 代劳
- 如果函数有多个 return 路径(比如 error early return),
defer能覆盖全部出口 - 不要在
Done()后再操作共享资源(如往结果 channel 写入),除非你已加锁或用其他同步机制保护
WaitGroup 可重用,但不能并发 Add + Wait 混用
很多人以为 WaitGroup{} 是一次性对象,其实只要 Wait() 返回后计数器为 0,它就回到了零值状态,可以再次 Add()。
- 重用没问题:
wg.Wait()返回后,直接wg.Add(5)启新一批任务是安全的 - 但禁止在
Wait()还没返回时,从另一个 goroutine 调用Add()—— 这会触发 panic “WaitGroup misuse” - 多个 goroutine 同时调用
Wait()是允许的,计数器归零时它们会一起被唤醒
别把 WaitGroup 当 channel 用,错误处理要另配机制
WaitGroup 只回答“是否全做完”,不回答“做得对不对”。常见错误是只等不收错:
- 想收集错误?得配
chan error或切片 + 互斥锁,WaitGroup本身不传值 - 想收返回结果?同样要额外 channel 或指针参数,
WaitGroup不持有任何业务数据 - 别试图用
WaitGroup控制超时——它没有 context 支持,超时得靠select+time.After或context.WithTimeout
*sync.WaitGroup 传参,声明时用 var wg sync.WaitGroup,传给函数时传 &wg。golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











