sync.waitgroup 是解决主协程提前退出问题的计数器工具,需在 goroutine 启动前调用 add,done 必须严格匹配且只调一次,禁止值拷贝,wait 后不可立即复用,也不负责结果收集与错误处理。

sync.WaitGroup 不是用来“学习协程语法”的工具,而是解决主协程提前退出这个具体问题的——只要没它,go func() { ... }() 启动的协程大概率根本没执行完,程序就退出了。
WaitGroup.Add 必须在 go 语句之前调用
这是最常卡死或漏执行的原因。Add 和 goroutine 启动之间没有同步保障,如果 Add 晚于或混在 goroutine 内部,Wait() 可能立刻返回(计数为 0),或者等不到 Done 就 panic。
- ✅ 正确:循环中先
wg.Add(1),再go func() { ... }() - ❌ 错误:
go func() { wg.Add(1); ...; wg.Done() }—— 计数错乱,Wait()无法感知任务存在 - ⚠️ 循环变量闭包陷阱:用
go func(j string) { ... }(job)传值,别直接引用job变量
Done 必须确保执行,且只能调用一次
Done() 是 Add(-1) 的快捷写法,调用次数必须和 Add() 总和严格匹配。少调用会死锁;多调用会 panic:sync: negative WaitGroup counter。
初始化并管理阿里云SDK Go 客户端,涵盖 sync.Once 单例、goroutine 安全、endpoint 与 region 配置、VPC 端点及调试。
- ✅ 强烈推荐
defer wg.Done(),无论函数从哪个分支 return 都能兜住 - ❌ 在 if 分支里条件性调用
wg.Done(),但某些路径遗漏 - ⚠️ 不要手动写
wg.Add(-1)替代Done(),语义不清且易出错
WaitGroup 不能复制,必须传指针
sync.WaitGroup 内部含 mutex 和原子状态,值拷贝会导致两个独立副本,一个调 Done(),另一个永远等不到——运行时报 sync: WaitGroup is reused before previous Wait has returned。
- ✅ 函数参数写
func worker(wg *sync.WaitGroup, job string) - ❌
func worker(wg sync.WaitGroup, ...)—— 值传递,复制结构体 - ⚠️ 切片
append、结构体字段赋值、返回值中都禁止直接放sync.WaitGroup值类型
Wait 之后不能立即复用,重用前需确认无 pending Wait
Go 1.20+ 对复用做了更严格检查:Wait() 返回后若直接 Add(),会 panic。这不是 bug,是设计约束——WaitGroup 不是线程安全的“重入式计数器”。
- ✅ 复用场景下,应新建
var wg sync.WaitGroup,或确保上一轮Wait()已彻底返回且无 goroutine 还在调Done() - ❌ 在同一个
wg上连续做两轮Add()→Wait(),中间无等待间隔 - ⚠️ 如果必须复用,可加注释说明“此处已确保无 goroutine 活跃”,但更推荐按需声明新变量,语义更清晰
真正容易被忽略的是:WaitGroup 只管“有没有做完”,不管“做没做对”。结果收集、错误处理、超时控制这些事,它一个都不干——得靠 channel、context.WithTimeout 或 sync.Mutex 配合。把它当计数器用,别当万能同步胶水。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










