waitgroup仅用于等待任务完成,不可控并发数;add必须在goroutine启动前调用且与done成对,推荐defer wg.done();需自行处理错误传递和并发安全;不可复用,每次应声明新实例。

WaitGroup 不是用来“控制并发数量”的,它只负责“等待完成”。想限流或排队,得配 semaphore 或 channel,别指望它。
WaitGroup 的 Add 和 Done 必须成对出现,且顺序不能错
常见错误是 Add 放在 goroutine 里、或者 Done 被漏掉/提前调用。一旦计数器变成负数,panic: sync: negative WaitGroup counter 就会立刻炸出来。
-
Add必须在go语句之前调用,确保主 goroutine 已登记任务数 -
Done推荐用defer wg.Done(),避免因 panic 或 return 早退导致漏调 - 如果传参给 goroutine,注意闭包变量捕获问题:写成
go func(i int) { ... }(i),而不是直接用循环变量i
WaitGroup 本身不带错误传递能力
它只管“有没有做完”,不管“做没做对”。想收集子任务的错误,得自己加 errChan 或共享 error 变量,但要注意并发安全。
- 不要在多个 goroutine 里直接写同一个
error变量,会竞态;要么用sync.Mutex保护,要么用带缓冲的chan error -
errChan缓冲大小建议设为len(files),避免 goroutine 因发送阻塞而卡住 -
close(errChan)必须在wg.Wait()之后,否则可能漏读
WaitGroup 不能复用,用完就得新建
官方文档明确说:WaitGroup 是一次性对象。重复调用 Wait() 或在 Wait() 返回后继续 Add(),行为未定义——实际中多数会 panic 或死锁。
- 不要把
sync.WaitGroup{}声明为全局变量或长期存活的 struct 字段 - 每个并发批次都该 new 一个:比如函数内声明
var wg sync.WaitGroup,用完即弃 - 如果真需要反复等,考虑封装成函数,每次调用都初始化新实例
最容易被忽略的是:WaitGroup 的零值是可用的,但它的内部计数器不是原子安全的——所以千万别把它作为结构体字段嵌入后直接赋值(如 myStruct.wg = sync.WaitGroup{}),这会破坏其底层 noCopy 检查,而且可能引发竞态。老老实实每次都在作用域里声明新变量。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











