WaitGroup.Done() 必须在所有执行路径(包括panic、error、recover)显式调用,且WaitGroup必须指针传递,否则会导致静默死锁;错误处理需结合errgroup或channel实现错误传播。

WaitGroup.Done() 必须在所有执行路径上触发
panic、return、error early exit 都可能跳过 defer wg.Done(),导致 Wait() 永远阻塞。这不是“偶尔漏掉”的小问题,而是只要有一条异常路径没覆盖,整个并发流程就卡死。
- 显式错误分支必须调用
wg.Done():比如 HTTP 请求失败、文件打开出错、JSON 解析失败等场景,不能只依赖 defer - panic 后 defer 仍会执行,但 recover 捕获后若未重新 panic,需手动补调
wg.Done() - 避免把
defer wg.Done()放在函数开头——它只对当前 goroutine 的退出生效,不保底覆盖所有子逻辑分支
recover 后的 wg.Done() 容易被忽略
当你用 recover() 拦截 panic 并选择“吞掉”错误继续执行时,defer wg.Done() 不会自动触发——因为 panic 已被 recover,goroutine 正常返回,但你可能忘了在 recover 块末尾加 wg.Done()。
- 错误写法:
defer wg.Done()在函数入口,recover 后直接 return,Done 只走一次(且可能被绕过) - 安全写法:在 recover 块内显式调用
wg.Done(),或改用带 error 返回的结构,统一在函数末尾处理 Done - 示例片段:
go func() { defer func() { if r := recover(); r != nil { log.Printf("panic: %v", r) wg.Done() // ⚠️ 必须手动补上 } }() // 可能 panic 的逻辑 riskyOperation() wg.Done() // 正常路径也走这里 }()
WaitGroup 和 error 处理天然不耦合
sync.WaitGroup 本身不记录任何错误,也不支持中断等待。你无法靠它知道“哪个 goroutine 出错了”或“是否该提前退出”。这是设计使然,不是缺陷,但容易让人误以为“等完就万事大吉”。
初始化并管理阿里云SDK Go 客户端,涵盖 sync.Once 单例、goroutine 安全、endpoint 与 region 配置、VPC 端点及调试。
- 单个 goroutine 错误不影响其他任务,但主流程可能因无错误感知而继续 Wait,直到全部完成(或超时)
- 想实现错误传播,必须额外引入 channel 或
errgroup.Group;errgroup是标准库x/sync/errgroup提供的 WaitGroup 超集,支持Go启动 +Wait返回首个 error - 不要试图用
wg.Add(-1)替代Done()来“撤销计数”——这会破坏计数器一致性,触发sync: negative WaitGroup counter
值传递 WaitGroup 导致 Done 失效的静默死锁
如果函数参数是 wg sync.WaitGroup(非指针),哪怕你在里面写了 defer wg.Done(),实际操作的是副本,main 中的 wg.Wait() 永远等不到计数归零——没有 panic,没有报错,程序只是卡住。
立即学习“go语言免费学习笔记(深入)”;
-
go vet能立刻发现:main.go:42: downloadFile passes sync.WaitGroup by value - 所有跨函数调用 WaitGroup 的地方,参数类型必须是
*sync.WaitGroup - struct 字段中若嵌入 WaitGroup,也必须是指针字段,否则方法接收者调用
Add/Done仍作用于副本

















