WaitGroup 更适合“等全部完成”场景,因其仅通过计数器实现同步,无需数据传递、缓冲管理或关闭处理;而 channel 易因接收次数不匹配(如启3个goroutine却只收2次)导致死锁或遗漏。

WaitGroup 为什么比 channel 更适合“等全部完成”
当你只需要确保 N 个 goroutine 都执行完再继续,sync.WaitGroup 是更直接、更轻量的选择。它不涉及数据传递,只做计数与阻塞,避免了通道的缓冲管理、关闭时机、接收次数匹配等额外负担。
常见错误现象:done := make(chan bool) 启动 3 个 goroutine 却只接收 2 次 ,主 goroutine 永久阻塞;或接收太多次,触发 panic: "send on closed channel"。
-
WaitGroup的Add()必须在go语句前调用(不能在 goroutine 内部),否则可能因竞态导致计数错乱 -
Done()推荐用defer wg.Done(),防止函数提前 return 时漏调 - 不要复用未重置的
WaitGroup实例;多次使用需配合Reset()(注意:Go 1.20+ 才支持)
channel 什么时候必须用,而不是 WaitGroup
当你要传递结果、错误、状态,或需要控制执行节奏(比如扇入、超时、取消),channel 就不可替代。WaitGroup 只回答“做完没”,而 channel 回答“做成什么样”“卡在哪”“要不要停”。
典型使用场景:生产者往 results chan Result 发送处理结果,主 goroutine 用 for res := range results 消费;或用 select + time.After 给单个任务加超时。
立即学习“go语言免费学习笔记(深入)”;
- 无缓冲 channel(
make(chan int))天然同步:发送即阻塞,直到有人接收 —— 适合一对一通知 - 带缓冲 channel(
make(chan int, 10))可解耦发送/接收节奏,但必须小心关闭时机,否则range永不退出 - 别在多个 goroutine 中同时关闭同一个 channel —— panic: "close of closed channel"
WaitGroup 和 channel 混合使用的合理模式
最稳健的高并发任务流,往往不是二选一,而是分工协作:WaitGroup 管“生命周期”,channel 管“数据流”。比如启动一批 worker 处理任务,用 WaitGroup 等它们全退出,再关闭结果 channel。
初始化并管理阿里云SDK Go 客户端,涵盖 sync.Once 单例、goroutine 安全、endpoint 与 region 配置、VPC 端点及调试。
示例关键片段:
results := make(chan Result, 100)
var wg sync.WaitGroup
<p>for i := 0; i < 5; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
for _, task := range getTasks(id) {
results <- process(task)
}
}(i)
}</p><p>// 单独 goroutine 等 wg 完成后关 channel
go func() {
wg.Wait()
close(results)
}()</p><p>// 主 goroutine 安全消费
for res := range results {
handle(res)
}这里 wg.Wait() 保证所有 sender 已退出,才调 close(results),避免 range 提前结束或 panic。
最容易被忽略的关闭 channel 时机
channel 关闭不是“谁先做完谁关”,而是“确认再无任何 goroutine 会向它发送”。哪怕只有一个 goroutine 还没退出,提前 close 就会导致 panic;反之,没人 close,range 就永远卡住。
所以真正安全的关闭点,几乎总是出现在一个独立 goroutine 里,且它等待的是所有 sender 的退出信号(通常靠 WaitGroup),而不是靠计数器或 sleep 估算。
如果 sender 数动态变化(比如从配置加载、网络发现),就得换用其他协调机制,比如 context 或原子标志位 —— 这时 WaitGroup 本身也不再适用。

















