select 里 ch 是指在 sql 查询语句中,ch 通常作为列别名(alias)或表别名使用,例如 select column as ch from table,用于简化引用或提高可读性。

select 里 ch
因为是否阻塞,取决于 channel 状态和是否有接收方在等待,不是 select 本身决定的。它只是“检查”——所有 case 中的发送/接收操作,在 select 求值阶段就已完成表达式计算(比如 v 已求值),但真正执行通信前会统一做就绪判断。
发送操作 ch 就绪的条件是:
- 若
ch是无缓冲 channel → 必须有 goroutine 正在执行(即已进入该 <code>case并等待); - 若
ch是带缓冲 channel → 缓冲区未满即可(哪怕没人等着收); - 若
ch已关闭 → 发送直接 panic,不会进入就绪判断。
所以,ch 在 <code>select 中“不阻塞”,本质是它满足了就绪条件,而非被 select 特殊豁免。
default 分支如何让 select 变成非阻塞轮询?
default 是唯一能打破阻塞的出口。只要存在 default,且所有其他 case 都未就绪,select 就立刻执行 default 并退出,整个过程零等待。
常见误用场景:
- 高频循环中写
for { select { case → 若 <code>ch长期空闲,CPU 会 100% 空转; - 误以为
default表示“通道没数据”,其实它只表示“此刻所有通信操作都不可立即完成”,包括发送端没 receiver、接收端 channel 为空且无 sender 等; - 忘记初始化 channel 就直接用在
select里 → 运行时 panic:invalid memory address or nil pointer dereference。
多个 case 同时就绪时,谁会被选中?
Go 运行时会从所有就绪的 case 中伪随机选择一个,不是按代码顺序,也不是优先级高低。这个随机性是刻意设计的,为避免 goroutine 饥饿或特定路径被持续抢占。
这意味着:
- 不能依赖
case的书写顺序来控制执行优先级; - 如果需要确定性顺序(比如优先处理超时),得靠逻辑拆分,例如把超时
case单独拎到外层select,或用time.After()配合; - 两个无缓冲 channel 同时有 goroutine 在收/发,它们的
case就算“同时就绪”,结果仍是随机的 —— 底层调度器不保证时序精确性。
为什么空 select{} 会永久挂起 goroutine?
select {} 是语法合法的特例:它没有 case,也没有 default。编译器会直接优化成调用 runtime.block(),最终触发 gopark,让当前 goroutine 进入休眠且永不唤醒。
这常用于主 goroutine 等待其他工作 goroutine 结束,但要注意:
- 它不释放任何资源,只是让 goroutine 停在那;
- 如果误写成
select{}而非select{ default: ... },本意是非阻塞却变成死锁; - 调试时看到 goroutine 状态为
waitreasonselectnocases,基本就是这里卡住了。
真正容易被忽略的是:select 的“非阻塞”完全依赖 default 存在与否,而不是 channel 类型或操作方向;而它的“随机性”只在竞争发生时生效,多数时候你面对的其实是确定性的就绪/不就绪判断。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











