英文:
Do select statements guarantee order of channel selection?
问题
根据这个答案,如果一个goroutine在两个通道上进行选择操作,是否保证通道的选择顺序与它们发送的顺序相同?我特别关注的是发送方是单线程的情况。
举个例子,是否保证这段代码始终产生相同的输出结果:
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)
}
我的实验似乎表明,对于无缓冲通道,如上所示,这是有保证的,但对于有缓冲通道,这是不确定的。请问有人可以确认一下吗?
英文:
Following on from this answer, if a goroutine is selecting on two channels, is it guaranteed that the channels are selected in the same order that they are sent on? I'm particularly interested in the case where the sender is single threaded.
As an example, is it guaranteed that this code always produces the same output:
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)
}
My experiments would seem to indicate that this is guaranteed for unbuffered channels, as shown, but not guaranteed for buffered channels. Can someone confirm that please?
答案1
得分: 9
不,这并不是保证的。实际上,它是随机选择的。根据Go语言规范(https://golang.org/ref/spec#Select_statements)(强调添加):
- 如果有一个或多个通信可以进行,将通过均匀的伪随机选择选择一个可以进行的通信。否则,如果有一个默认情况,将选择该情况。如果没有默认情况,"select"语句将阻塞,直到至少有一个通信可以进行。
英文:
No. It's not guaranteed. In fact, it's randomized. From the Go language spec (emphasis added):
> 2. If one or more of the communications can proceed, a single one that can proceed is chosen via a uniform pseudo-random selection. Otherwise, if there is a default case, that case is chosen. If there is no default case, the "select" statement blocks until at least one of the communications can proceed.
答案2
得分: 7
虽然在你的示例中,可以保证先打印出1
,但这并不是因为select
的工作方式。
这是因为x
是一个无缓冲通道,对它的发送操作会阻塞,直到有其他协程从中接收数据,然后才能发送到通道y
。
因此,在worker()
函数内部的select
语句不会同时看到两个准备好的通道。
如果可以同时看到两个准备好的通道,那么其中一个会被伪随机地选择。详细信息请参见https://stackoverflow.com/questions/47645808/how-does-select-work-when-multiple-channels-are-involved/47648910#47648910
因此,请注意,如果你使用带缓冲的通道:
x = make(chan int, 1)
y = make(chan int, 1)
那么对x
和y
的发送操作将不会阻塞,现在由协程调度器决定协程的执行顺序。在worker()
协程到达并评估通道操作之前,主协程有可能先发送两个值,因此你可能会看到先打印出1
然后是2
,或者先打印出2
然后是1
。
英文:
Although in your example it's guaranteed that you'll see 1
printed first, but not because of how select
works.
It's because x
is an unbuffered channel, and send on it will block until someone receives from it, and only then can you send on channel y
.
So your select
inside worker()
won't see 2 ready channels at the same time.
It if would, then one is chosen pseudo-randomly. For details, see https://stackoverflow.com/questions/47645808/how-does-select-work-when-multiple-channels-are-involved/47648910#47648910
So note that if you'd use buffered channels:
x = make(chan int, 1)
y = make(chan int, 1)
Then send on x
and y
wouldn't block, and now it's up to the goroutine scheduler how goroutines are executed. It's possible the main
goroutine can send both values before the worker()
goroutine reaches and evaluates the channel operations, and so you could see 1
then 2
printed, just as well as 2
and then 1
.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论