Go channel 与 select:编排而不是堆砌
goroutine 容易开,难在收口。channel 与 select 是把并发从「能跑」变成「可推理」的关键。
Go 的并发哲学常被概括为:用通信共享内存。落地时,多数问题出在:谁负责关闭、如何超时、如何把多路结果合成一路。
关闭约定
- 只由发送方关闭;接收方关闭会导致 panic。
- 关闭表示「不会再有新值」,可被多次接收读到零值与
ok=false。 - 若有多个发送方,需要额外同步(如 WaitGroup + 单一 closer)。
select 的四种常用形
- 多路等待:同时监听多个 channel。
- 超时:
time.After或带 deadline 的 context。 - 默认分支:非阻塞探测,小心忙等。
- 取消:监听
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 更重要。