select 同时监听多个 channel 操作,阻塞直到至少一个 case 可执行。多个 case 同时就绪时随机选一个执行——这是刻意设计,避免固定优先级造成的饥饿。所有 case 都不可执行且有 default 时走 default(用于非阻塞收发、轮询);没有 default 则阻塞;监听的 channel 全为 nil 时永久阻塞(可能触发死锁检测)。
执行细节:case 中的发送右值和接收表达式在进入 select 时就会统一求值;select 配合 for 构成事件循环是 Go 并发的标准骨架。
for {
select {
case msg := <-in:
handle(msg)
case <-ticker.C:
flush()
case <-ctx.Done():
return // 退出事件循环
}
}
易错点:循环里用 time.After 每次迭代都新建 timer,高频循环会造成 timer 堆积,应改用 time.Ticker 或复用 Timer.Reset;case 里执行耗时操作会阻塞整个事件循环,必要时开 goroutine 或用缓冲解耦。追问方向:用双层 select 实现优先级、nil channel 禁用分支技巧。