
sync.WaitGroup 是 Go 中轻量、高效的等待组工具,适用于“主线程等待所有 goroutine 完成”的典型场景;它不替代通道通信,而是与通道协同——前者解决等待同步,后者解决数据传递与协作逻辑。
`sync.waitgroup` 是 go 中轻量、高效的等待组工具,适用于“主线程等待所有 goroutine 完成”的典型场景;它不替代通道通信,而是与通道协同——前者解决**等待同步**,后者解决**数据传递与协作逻辑**。
在 Go 并发编程中,sync.WaitGroup 的核心职责是计数式同步:通过 Add()、Done() 和 Wait() 三步,精确控制主线程对一组 goroutine 生命周期的等待。它并非用于传递数据或协调复杂状态,因此 Go 官方文档明确指出:“除 Once 和 WaitGroup 外,多数 sync 包类型面向底层库设计;更高级的同步应优先使用通道(channels)”。
你提供的原始用法逻辑基本正确,但存在两个关键隐患,需立即修正:
✅ 正确写法(推荐)——避免变量捕获陷阱
var wg sync.WaitGroup
func send(a, b string) {
defer wg.Done()
// 模拟实际工作,例如 HTTP 请求或文件处理
time.Sleep(100 * time.Millisecond)
fmt.Printf("Processed: %s, %s\n", a, b)
}
func main() {
for i := 0; i <p>⚠️ <strong>原始代码的问题分析</strong>: </p>
- ❌
wg.Add(1)放在循环内但未加锁保护(虽此处无竞态,但易误用); - ❌
go send(...)直接启动,但send函数中defer wg.Done()依赖闭包捕获——若循环变量i被复用(如for i := range),会导致Done()调用次数错误; - ❌ 缺少超时机制,
wg.Wait()可能永久阻塞(如某 goroutine panic 未执行Done())。
? 进阶实践:WaitGroup + Channel 实现带超时的优雅等待
当需要响应中断、设置超时或与其他事件联动时,应将 WaitGroup 与通道结合使用(而非取代):
func main() {
var wg sync.WaitGroup
done := make(chan struct{})
// 启动 goroutines
for i := 0; i <p>? <strong>关键原则总结</strong>: </p>
- ✅
WaitGroup是同步原语,用于“等待结束”,不是“通信管道”; - ✅ 通道(channel)是通信原语,用于“传递数据、信号、错误或控制流”;
- ✅ 真实项目中二者常共存:用
WaitGroup确保资源清理时机,用 channel 汇总结果或传播错误; - ✅ 永远在
go语句之前调用wg.Add(1),并在每个 goroutine 内确保wg.Done()执行(建议用defer); - ✅ 生产环境务必添加超时、panic 恢复或健康检查,避免
WaitGroup成为单点故障源。
简言之:WaitGroup 是你的“并发计时器”,而 channel 是你的“并发信使”——各司其职,协同增效。










