先向c1发送数据,为何偶现select先接收c2的情况?
Go中select语句的随机选择行为解析
package main func m() { c1 := make(chan int, 1) c2 := make(chan int, 1) go func() { c1 <- 1 c2 <- 1 }() select { case <-c1: case <-c2: println("no way") } } func main() { for i := 0; i < 1000000; i++ { m() } }
我们创建了两个带缓冲的Channel:c1和c2(缓冲大小为1)。在一个goroutine中依次向c1、c2发送数据,同时主goroutine通过select语句尝试从这两个Channel接收数据并退出。
多数情况下先发送c1就会先接收c1,但偶尔会出现先发送c1却先接收c2的情况,请问原因是什么?
这其实是Go语言select语句的既定设计行为导致的:
- 首先,我们创建的是带缓冲(大小1)的channel,向这类channel发送数据时,只要缓冲未满,操作就是非阻塞的。所以子goroutine里的
c1 <- 1和c2 <- 1会非常快地连续完成——快到主goroutine的select语句执行时,c1和c2两个channel都已经处于就绪状态(都有数据可以接收)。 - 当
select语句的多个case对应的channel都就绪时,Go语言的运行时会随机选择其中一个case执行,而不是严格按照代码里的书写顺序来优先选择第一个就绪的channel。
所以当两个channel都就绪时,select有一定概率选中case <-c2,这时候就会触发println("no way"),也就是你看到的“偶尔出现先发送c1却先接收c2”的情况。如果把循环次数调大,你会发现"no way"的打印次数大概接近总次数的一半(因为是随机选择,不会绝对精确)。
内容的提问来源于stack exchange,提问作者Sveargith
相关产品推荐
相关产品推荐

