Go channel写入顺序保证:select多case是否优先执行先就绪的通道
问题解答
前置说明
你给出的代码存在两处可直接导致运行异常的笔误/逻辑问题:
- 子goroutine中的
fmr.Println应为fmt.Println,否则会编译报错 - 全程没有对
done通道执行写入或者关闭操作,程序最终会永久阻塞在主goroutine的<-done语句,无法正常退出
以下讨论基于修正上述问题后的代码展开。
核心问题答复
1. 程序是否保证永远打印"ch1"?
不保证,存在打印"ch2"的可能性。
2. 为什么第二个case可能先执行?
这里的关键在于Go调度器的调度时机以及select语句的执行规则:
- 主goroutine执行
<-ready之后,子goroutine已经进入select阻塞状态,等待任意一个case就绪 - 主goroutine先执行
ch1 <- struct{}{},此时ch1的读case就绪;接下来执行close(ch2),已关闭的通道永远可以读,因此ch2的读case也进入就绪状态 - 存在两种可能的调度时序:
- 第一种:主goroutine写完ch1之后,子goroutine立刻被调度唤醒,此时ch2还未关闭,只有ch1的case就绪,因此打印"ch1"
- 第二种:主goroutine写完ch1之后没有发生调度,连续执行完
close(ch2),之后子goroutine才被调度唤醒,此时两个case都处于就绪状态,select会随机选择一个执行,就有可能选到ch2的case
3. 官方与源码依据
- 官方规范:Go语言官方规范中明确说明,当select语句同时有多个可执行的case时,运行时会伪随机选择其中一个执行,不存在优先级排序。
- 源码实现:Go runtime层的select实现逻辑中,会先收集所有就绪的case,打乱顺序后选择第一个执行,没有按照case定义顺序选择的逻辑。
内容的提问来源于stack exchange,提问作者shmth
相关产品推荐
相关产品推荐

