sync.WaitGroup
是 Go 语言标准库 sync
包中的一个并发原语,用于等待一组 Goroutine 完成执行。它通过计数器机制实现协程的同步,常用于主 Goroutine 阻塞等待所有子 Goroutine 完成任务后再继续执行。
核心方法
Add(delta int)
- 增加或减少 WaitGroup 的计数器值(通常为正数)。
- 在启动新 Goroutine 前调用,表示需要等待的任务数。
delta
可以是负数(但需确保计数器不为负)。
Done()
- 将计数器减 1(等价于
Add(-1)
)。 - 在 Goroutine 内部 defer 调用,确保任务完成后通知 WaitGroup。
- 将计数器减 1(等价于
Wait()
- 阻塞当前 Goroutine,直到计数器归零。
- 通常由主 Goroutine 调用。
使用示例
package main
import (
"fmt"
"sync"
"time"
)
func worker(id int, wg *sync.WaitGroup) {
defer wg.Done() // 确保任务完成后计数器减1
fmt.Printf("Worker %d starting\n", id)
time.Sleep(time.Second) // 模拟耗时任务
fmt.Printf("Worker %d done\n", id)
}
func main() {
var wg sync.WaitGroup
for i := 1; i <= 3; i++ {
wg.Add(1) // 每个任务开始前增加计数器
go worker(i, &wg)
}
wg.Wait() // 阻塞直到所有任务完成
fmt.Println("All workers completed")
}
关键注意事项
指针传递
WaitGroup 必须通过指针传递给 Goroutine,否则会导致副本操作,引发死锁。计数器的正确性
Add()
必须在Wait()
之前调用(通常在启动 Goroutine 前)。Done()
次数必须与Add()
的总增量匹配,否则会 panic。
避免竞态条件
不要在多个 Goroutine中并发调用Add()
(建议在主 Goroutine中集中管理)。不可重用
一个 WaitGroup在计数器归零后不能重复使用(除非重新初始化)。
典型应用场景
- 批量异步任务等待:如并发处理一批文件下载、数据库查询等。
- 服务优雅退出:等待所有后台 Goroutine清理资源后再关闭程序。
- 测试多线程逻辑:确保所有测试用例的并发操作完成后再校验结果。
如果涉及更复杂的同步需求(如错误传递),可结合 channel
或 errgroup.Group
(扩展库)使用。
内容由零声教学AI助手提供,问题来源于学员提问