sync.WaitGroup用于等待一组goroutine完成,通过Add增加计数、Done减少计数、Wait阻塞至计数归零,适用于批量任务同步,如并发请求处理。

在Go语言中,sync.WaitGroup 是一种常用的同步机制,用于等待一组并发的goroutine执行完成。它特别适合在主协程中启动多个子协程并等待它们全部结束的场景,比如批量任务处理、并发请求等。
WaitGroup基本用法
WaitGroup 通过计数器控制等待逻辑:调用 Add(n) 增加等待数量,每个goroutine执行完后调用 Done() 减一,主协程通过 Wait() 阻塞直到计数器归零。
典型使用结构如下:
var wg sync.WaitGroup
for i := 0; i < 5; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
// 模拟任务
fmt.Printf("Goroutine %d 正在执行\n", id)
}(i)
}
wg.Wait() // 等待所有goroutine完成
fmt.Println("全部完成")
关键点:
立即学习“go语言免费学习笔记(深入)”;
- Add 必须在 go语句之前 调用,否则可能产生竞态条件
- Done 通常用 defer 确保一定会执行
- Wait 放在主协程最后调用,阻塞直到所有任务结束
常见错误与注意事项
使用 WaitGroup 时容易出错的地方包括:
- Add 调用时机错误:如果在 goroutine 内部才 Add,可能导致主协程 Wait 提前返回
- 重复 Done:一个 goroutine 多次 Done 会导致计数器负数,panic
- 忘记 Done:某个 goroutine 没调用 Done,导致主协程永远阻塞
正确做法是:在启动 goroutine 前就确定任务数量,并提前 Add。
实际应用场景示例
假设我们要并发获取多个API接口数据:
urls := []string{"http://a.com", "http://b.com", "http://c.com"}
results := make([]string, len(urls))
var wg sync.WaitGroup
for i, url := range urls {
wg.Add(1)
go func(i int, u string) {
defer wg.Done()
resp, err := http.Get(u)
if err != nil {
results[i] = "error"
return
}
results[i] = resp.Status
}(i, url)
}
wg.Wait()
fmt.Println("结果:", results)
这里通过 WaitGroup 确保所有HTTP请求完成后再输出结果,避免了主协程提前退出的问题。
与channel的对比选择
WaitGroup 适用于只需要“等待完成”而不需要返回值的场景。如果需要收集每个goroutine的结果,结合 channel 使用更合适:
- 只关心完成:用 WaitGroup
- 需要返回数据:WaitGroup + channel 或直接用 channel 控制
- 有超时需求:建议用 context 配合 channel 实现









