




因为多个生产者并发写入时执行顺序不可预测,若任一生产者提前关闭通道,其余生产者将panic。
close(ch)
因为多个生产者并发写入时,谁先执行完、谁后执行完不可预测。如果第一个生产者写完就 close(ch),后面其他生产者继续 ch 就会 panic:send on closed channel。
panic: send on closed channel
sync.WaitGroup 跟踪所有生产者,等它们全部调用 wg.Done() 后,再由一个独立 goroutine 执行 close(ch)
wg.Wait(); close(ch) —— 这会阻塞主线程,消费者无法并行启动缓冲区不是越大越好,也不是越小越安全;它本质是内存与吞吐的权衡点。
make(chan int, 1) 等价于无缓冲 channel,退化为同步模式,吞吐低、易卡死make(chan int, 1000) 可能导致任务积压、OOM 或延迟不可控(比如消费者卡住,1000 个任务全堆在内存里)
make(chan Task, 5) 到 make(chan Task, 10)
select { case ch ,不能依赖缓冲自动淘汰
for range ch 就够了吗够,但前提是 channel 关闭时机绝对正确。否则 range 会永远阻塞,或提前退出漏数据。
for data := range ch 是最简洁安全的写法 —— channel 关闭后自动退出循环,无需手动检查 ok
ch 时,range 不会造成竞争,Go runtime 保证每个值只被一个消费者收到sync.WaitGroup 管理它们,不能复用生产者的 wg
裸 channel 只解决通信,不解决健壮性。上线前至少补这三块:
select { case ch
context.Context 传给消费者,在处理任务时用 ctx.WithTimeout 防止单任务卡死整个 workerchan error 发送错误,主 goroutine 统一收集重试或告警;别让错误静默消失最容易被忽略的是「关闭 channel 的 goroutine 是否真的跑完了」——它没有返回值、不参与 WaitGroup,一旦 panic 或逻辑错,整个流程就卡死在 range 里,连日志都打不出来。