select语句是否保证通道选择的顺序?



根据这个答案,如果一个程序选择两个通道,它是否保证通道的选择顺序与它们发送的顺序相同?我对发送方是单线程的情况特别感兴趣。

作为一个例子,是否保证这段代码总是产生相同的输出:

package main
import (
"fmt"
"time"
)
var x, y chan int
func worker() {
for {
select {
case v := <- x:
fmt.Println(v)
case v := <- y:
fmt.Println(v)
}
}
}
func main() {
x = make(chan int)
y = make(chan int)
go worker()
x <- 1
y <- 2
<- time.After(1 * time.Second)
}

我的实验似乎表明,这对于未缓冲的通道是有保证的,如图所示,但对于缓冲的通道则不能保证。有人能证实一下吗?

No。这不能保证。事实上,它是随机的。来自Go语言规范(强调添加):

  1. 如果一个或多个通信可以进行,则通过统一的伪随机选择选择一个可以进行的通信. 否则,如果存在默认情况,则选择该情况。如果没有默认情况,则"select";语句阻塞,直到至少一个通信可以继续。

虽然在您的示例中可以保证您将首先看到1,但不是因为select的工作方式。

这是因为x是一个非缓冲通道,在它上发送会阻塞,直到有人从它接收,只有这样你才能在y通道上发送。

所以你的selectworker()内不会同时看到2个就绪通道。

如果可以,则伪随机选择一个。具体操作请参见多通道时选择如何工作?

所以请注意,如果你要使用缓冲通道:

x = make(chan int, 1)
y = make(chan int, 1)

然后在xy上发送不会阻塞,现在它取决于线程调度程序如何执行线程。有可能main运行例程可以在worker()运行例程到达并计算通道操作之前发送两个值,因此您可以看到1然后2打印出来,就像2然后1一样。

最新更新