症状
程序要么以 all goroutines are asleep - deadlock! 死锁,要么 panic: sync: negative WaitGroup counter。两者都指向计数器误用:在 Wait 之后或 goroutine 启动之后才 Add,按值复制 WaitGroup 使每份副本各有自己的计数器,或在错误路径上跳过 Done。运行时消息给出机制,根因是顺序。
诊断
用 -race 暴露顺序问题;检查 Add/Wait/Done 的位置;用 go vet 检测按值复制。
go run -race main.go
根因
WaitGroup 计数器未反映已启动的工作:Add 太晚、值被复制,或 Done 被跳过,于是 Wait 要么永久阻塞、要么发现负计数。
修复
在启动其核算的 goroutine 之前且在任何 Wait 之前调用 Add;以指针传递或闭包捕获而非复制;用 defer 确保每条退出路径都调用 Done。用竞态检测与 go vet 捕获常见变体。
验证
在有与无竞态检测两种情形下运行,确认程序完成且无死锁或 panic,并确认所有 worker 都产出了结果。完成且输出正确即证明计数已反映工作。
常见陷阱
把 WaitGroup 作为结构体字段按值传递给方法会复制计数器;应使用指针接收者。