select 语句是否保证通道 selection 的顺序?
Do select statements guarantee order of channel selection?
继 this answer 之后,如果一个 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)
}
我的实验似乎表明这对于无缓冲通道是有保证的,如图所示,但对于缓冲通道则不能保证。有人可以确认一下吗?
没有。这不能保证。事实上,它是随机的。来自 Go language spec(强调已添加):
- 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.
虽然在您的示例中可以保证您会首先看到 1
,但不是因为 select
的工作方式。
因为x
是一个无缓冲的通道,在它上面发送会阻塞,直到有人从它那里接收,然后你才能在通道y
上发送。
因此 worker()
中的 select
不会同时看到 2 个就绪频道。
如果愿意,则伪随机选择一个。详情见
所以请注意,如果您使用缓冲频道:
x = make(chan int, 1)
y = make(chan int, 1)
然后在 x
上发送并且 y
不会阻塞,现在由 goroutine 调度器决定如何执行 goroutines。 main
goroutine 可以在 worker()
goroutine 到达并评估通道操作之前发送这两个值,因此您可以看到 1
然后 2
被打印出来,就像2
然后 1
.
继 this answer 之后,如果一个 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)
}
我的实验似乎表明这对于无缓冲通道是有保证的,如图所示,但对于缓冲通道则不能保证。有人可以确认一下吗?
没有。这不能保证。事实上,它是随机的。来自 Go language spec(强调已添加):
- 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.
虽然在您的示例中可以保证您会首先看到 1
,但不是因为 select
的工作方式。
因为x
是一个无缓冲的通道,在它上面发送会阻塞,直到有人从它那里接收,然后你才能在通道y
上发送。
因此 worker()
中的 select
不会同时看到 2 个就绪频道。
如果愿意,则伪随机选择一个。详情见
所以请注意,如果您使用缓冲频道:
x = make(chan int, 1)
y = make(chan int, 1)
然后在 x
上发送并且 y
不会阻塞,现在由 goroutine 调度器决定如何执行 goroutines。 main
goroutine 可以在 worker()
goroutine 到达并评估通道操作之前发送这两个值,因此您可以看到 1
然后 2
被打印出来,就像2
然后 1
.