当 Goroutine 需要等待:Go 中 sync.WaitGroup 的优雅使用
在 Go 并发编程中,我们经常遇到一个经典场景:启动多个 goroutine 执行任务,主程序需要等待所有任务完成后再继续。这时,sync.WaitGroup 就成为了我们的得力助手。
核心原理
WaitGroup 本质上是一个计数器。通过 Add() 增加计数,Done() 减少计数,Wait() 阻塞直到计数归零。这种简洁的 API 设计让并发等待变得异常清晰。
典型使用模式
var wg sync.WaitGroup
for _, task := range tasks {
wg.Add(1)
go func(t Task) {
defer wg.Done()
process(t)
}(task)
}
wg.Wait()
fmt.Println("所有任务完成")
实用场景
- 并行处理批量数据(如图片处理、文件上传)
- 微服务中并发调用多个下游接口
- 测试用例中的并发验证
- 服务启动时的初始化协程
关键注意事项
- Add 要在 goroutine 外调用:避免竞态条件
- 使用 defer wg.Done():确保即使 panic 也能递减计数
- 不要复制 WaitGroup:传递时使用指针
- 计数归零后 WaitGroup 可复用:通过 Add 重新设置计数
进阶技巧
结合 context 实现带超时的等待:
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
done := make(chan struct{
})
go func() {
wg.Wait()
close(done)
}()
select {
case <-done:
// 正常完成
case <-ctx.Done():
// 超时处理
}
WaitGroup 体现了 Go 的设计哲学:用简单的抽象解决复杂问题。掌握它,你就能优雅地驾驭并发等待场景,写出更健壮的 Go 程序。
记住:在并发世界中,有时等待比执行更需要智慧。