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(强调已添加):

  1. 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.