Go语言select语句是否保证按通道发送顺序选择就绪通道?
Go select通道选择顺序官方结论
首先明确:Go语言规范从未承诺select会按照通道发送操作的先后顺序选择就绪的case,你观测到的无缓冲通道场景下的顺序一致只是当前版本Go运行时的实现+无缓冲通道同步特性共同作用的结果,绝对不能作为通用逻辑依赖。
你给出的示例表现原因
你的测试代码中x、y都是无缓冲通道,单线程发送的场景下:
- 执行
x <- 1时,发送操作会阻塞,直到worker goroutine的select选中x的接收case、完成数据传输之后,main goroutine才会继续执行下一行y <- 2 - 等
y <- 2执行时,worker已经回到下一轮select循环,此时只有y的接收case就绪,自然会接收2输出 - 这个场景下的顺序一致性是无缓冲通道的阻塞同步特性带来的,和select的选择逻辑没有关系。
缓冲通道无顺序保证的原因
当你把x、y改成带缓冲的通道(比如make(chan int, 1)):
x <-1和y <-2都可以立刻写入缓冲、不会阻塞,main goroutine会在极短时间内完成两个写入操作- 此时worker的select会遇到两个同时就绪的接收case,按照Go规范要求,运行时会均匀随机选择其中一个case执行,所以输出可能是1在前也可能是2在前,没有任何固定顺序。
单线程发送场景的通用规则
哪怕发送方是单线程,只要满足以下两个条件之一,就可能出现顺序不确定的情况:
- 通道带缓冲,且发送操作写入时缓冲未被填满,不会触发发送阻塞
- 多个发送操作和select接收操作的执行时序存在重叠,导致多个通道同时就绪
如果你的业务逻辑必须保证接收顺序和发送顺序一致,不要依赖select的隐式特性,应该用单个通道传输带顺序标记的结构体,或者用单独的同步原语控制收发时序,示例实现如下:
// 顺序保证的推荐实现示例 type Msg struct { Seq int Val int } var ch = make(chan Msg) // 发送方按顺序发送 ch <- Msg{Seq:1, Val:1} ch <- Msg{Seq:2, Val:2} // 接收方直接按接收顺序处理即可
内容的提问来源于stack exchange,提问作者Tom
相关产品推荐
相关产品推荐

