sync.WaitGroup 是什么.
sync.WaitGroup 是 Go 标准库中用于等待一组 goroutine 完成的同步原语。它解决的核心问题是:主 goroutine 需要知道其他多个 goroutine 何时全部执行完毕。
基本用法
| |
为什么需要 Add 和 Done
WaitGroup 内部维护一个计数器,它的语义是"还有多少个任务没完成":
Add(delta):告诉 WaitGroup “我要开始delta个任务了”,计数器增加。Done():等价于Add(-1),表示"我完成了 1 个任务",计数器减少。Wait():阻塞当前 goroutine,直到计数器归零。
拆成两个方法而不是一个"注册+运行"的 API,是因为任务的启动和完成在时间上是分离的:
- 你需要在启动 goroutine 之前就
Add,否则可能出现 goroutine 已经跑完并Done了、但Add还没执行的竞态,导致计数器错乱甚至Wait永远不返回。 Done必须在任务真正结束时调用,通常配合defer,保证即使 panic 或提前 return 也能正确减计数。
关键细节
Add 必须在 Wait 之前、且在 goroutine 启动前调用
| |
正确写法是在 go 语句之前 wg.Add(1)。
计数器不能变成负数:Done 调用次数多于 Add 会 panic。
WaitGroup 不可复制:它内部含 noCopy 标记,传参要用指针 *sync.WaitGroup。
复用:等 Wait 返回、计数器归零后,可以再次 Add 复用同一个 WaitGroup(但要注意此时不能有并发的 Wait)。
和其他原语的区别
| 原语 | 用途 |
|---|---|
sync.WaitGroup | 等待一组任务全部完成 |
sync.Mutex | 互斥访问共享资源 |
| channel | 更通用的通信/同步,可实现类似 WaitGroup 的效果但更灵活 |
简单说,Add/Done 的设计是为了把"声明任务数量"和"标记任务结束"这两个时机分开,既保证计数正确,又让完成通知可以自然地用 defer 表达。