Go channel 与 select:编排而不是堆砌

goroutine 容易开,难在收口。channel 与 select 是把并发从「能跑」变成「可推理」的关键。

Go 的并发哲学常被概括为:用通信共享内存。落地时,多数问题出在:谁负责关闭、如何超时、如何把多路结果合成一路。

关闭约定

  • 只由发送方关闭;接收方关闭会导致 panic。
  • 关闭表示「不会再有新值」,可被多次接收读到零值与 ok=false
  • 若有多个发送方,需要额外同步(如 WaitGroup + 单一 closer)。

select 的四种常用形

  1. 多路等待:同时监听多个 channel。
  2. 超时:time.After 或带 deadline 的 context。
  3. 默认分支:非阻塞探测,小心忙等。
  4. 取消:监听 ctx.Done(),让退出路径显式可见。
select {
case v := <-out:
    handle(v)
case <-ctx.Done():
    return ctx.Err()
case <-time.After(timeout):
    return errTimeout
}

扇出与扇入

扇出:一份任务切给多个 worker;扇入:多个结果汇入一个 channel。记得限制 worker 数量,并用 WaitGroup 在关闭汇入 channel 前等待。

小结

先画清「谁生产、谁消费、谁关闭、如何取消」,再写 select。编排清晰,比多开几个 goroutine 更重要。