select语句必须配合channel使用,不能单独运行;它专为channel操作设计,每个case只能是channel的发送或接收操作,否则编译报错。
select 语句必须配合 channel 使用,不能单独运行
Go 的 select 不是 switch,也不是控制流分支语句,它专为 channel 操作设计。如果你写了一个没有 case 引用 channel 的 select,编译会直接报错:invalid operation: select (no cases)。常见误写是把变量判断塞进 case,比如 case x > 0: —— 这在 Go 里非法,case 后只能是 channel 的发送或接收操作。
正确用法只有一种模式:每个 case 必须是 ch 或 形式。例如:
select {
case msg := <-ch1:
fmt.Println("received", msg)
case ch2 <- "hello":
fmt.Println("sent")
default:
fmt.Println("no ready channel")
}
-
select会**随机选择一个就绪的case** 执行(不是按顺序),避免隐式优先级依赖 - 所有 channel 操作都是**非阻塞检查**;没就绪时,若无
default,goroutine 会挂起等待 - 多个
case同时就绪?Go 运行时随机挑一个,不保证 FIFO
nil channel 在 select 中永远阻塞,可用来动态关闭分支
给 channel 赋值 nil 后再放进 select,对应 case 就永久不可达。这不是 bug,而是 Go 提供的“逻辑禁用”手段。比如你有个定时器 channel tick,想在某个条件满足后停止响应它,只需把它设为 nil:
var tick <-chan time.Time
if shouldStopTicker {
tick = nil // 此后该 case 永远不会被选中
}
select {
case <-ch: // 正常接收
case <-tick: // 若 tick == nil,则此 case 被忽略
}
- 赋
nil是安全的,不会 panic;但对nilchannel 做 send/receive 操作会 panic,仅select中例外 - 别用
close(ch)替代ch = nil—— 关闭的 channel 仍可读(返回零值),而nil是彻底屏蔽 - 这个技巧常用于连接管理、超时切换、条件订阅等场景
default 分支不是“兜底逻辑”,而是非阻塞轮询的关键
有 default 的 select 等价于“尝试执行一次,不等”。它不表示“其他 case 都失败了才执行”,而是只要进入 select,就**立即检查所有 channel 是否就绪,任意一个就绪就走对应 case;全不就绪就走 default**。没有 default,就会阻塞。