select 语句能保证通道选择的顺序吗?

Tom*_*Tom 1 channel go goroutine

此答案之后,如果一个 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)
}
Run Code Online (Sandbox Code Playgroud)

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

Fli*_*mzy 8

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

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


icz*_*cza 7

尽管在您的示例中保证您会1首先看到打印,但不是因为select工作原理。

这是因为x是一个无缓冲的通道,在它上面发送会阻塞,直到有人从它接收到,然后你才能在 channel 上发送y

因此,您的select内部worker()不会同时看到 2 个就绪通道。

如果愿意,则伪随机选择一个。详情请参见多通道时select如何工作?

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

x = make(chan int, 1)
y = make(chan int, 1)
Run Code Online (Sandbox Code Playgroud)

然后继续发送x并且y不会阻塞,现在由 goroutine 调度程序决定 goroutines 的执行方式。这是可能的main够程可在发送前两个值worker()够程到达和评估渠道运作,所以你可以看到1,然后2打印出来,一样好2,然后1