单个通道上的多个接收器。谁得到数据?

Multiple receivers on a single channel. Who gets the data?

无缓冲通道会阻塞接收器,直到通道上的数据可用为止。我不清楚这种阻塞在同一通道上如何处理多个接收器(比如使用 goroutines 时)。我敢肯定,只要该通道上没有发送数据,它们都会阻塞。
但是一旦我向该通道发送单个值会发生什么?哪个 receiver/goroutine 将获取数据并因此解锁?他们都?排在第一位?随机?

一个随机(非确定性)的人会收到它。

查看语言 spec:

Execution of a "select" statement proceeds in several steps:

  1. For all the cases in the statement, the channel operands of receive operations and the channel and right-hand-side expressions of send statements are evaluated exactly once, in source order, upon entering the "select" statement. The result is a set of channels to receive from or send to, and the corresponding values to send. Any side effects in that evaluation will occur irrespective of which (if any) communication operation is selected to proceed. Expressions on the left-hand side of a RecvStmt with a short variable declaration or assignment are not yet evaluated.
  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.
  3. Unless the selected case is the default case, the respective communication operation is executed.
  4. If the selected case is a RecvStmt with a short variable declaration or an assignment, the left-hand side expressions are evaluated and the received value (or values) are assigned.
  5. The statement list of the selected case is executed.

默认情况下,goroutine 通信是 synchronousunbuffered:只有接收方接受该值,发送才会完成。必须有一个接收方准备好从通道接收数据,然后发送方可以将数据直接交给接收方。

因此通道 send/receive 操作阻塞,直到另一端准备就绪:

1. 通道上的发送操作阻塞,直到接收方可用于同一通道:如果 ch 上的值没有接收方,则没有其他接收方值可以放在通道中。反之亦然:当通道不为空时,ch 中不能发送新值!因此发送操作将等到 ch 再次可用。

2. 通道的接收操作阻塞,直到发送方可用于同一通道:如果通道中没有值,则接收方阻塞。

以下示例对此进行了说明:

package main
import "fmt"

func main() {
    ch1 := make(chan int)
    go pump(ch1) // pump hangs
    fmt.Println(<-ch1) // prints only 0
}

func pump(ch chan int) {
    for i:= 0; ; i++ {
        ch <- i
    }
}

因为没有接收者,goroutine 挂起并只打印第一个数字。

为了解决这个问题,我们需要定义一个新的 goroutine,它在无限循环中从通道中读取数据。

func receive(ch chan int) {
    for {
        fmt.Println(<- ch)
    }
}

然后在main():

func main() {
    ch := make(chan int)
    go pump(ch)
    receive(ch)
}

如果程序允许多个 goroutines 在单个通道上接收,则发送方正在广播。每个接收器都应该能够同等地处理数据。因此,go 运行time 使用什么机制来决定众多 goroutine 接收者中的哪一个 运行 Cf 并不重要。 https://github.com/golang/go/issues/247。但是如果通道没有缓冲,每个发送的项目只有一个 运行。

There have been some discussion about this

但是Go Memory Model中规定的是最多是其中之一。

Each send on a particular channel is matched to a corresponding receive from that channel, usually in a different goroutine.

这不像我想要的那样清晰,但后来他们给出了这个信号量实现的例子

var limit = make(chan int, 3)

func main() {
    for _, w := range work {
        go func(w func()) {
            limit <- 1
            w()
            // if it were possible for more than one channel to receive
            // from a single send, it would be possible for this to release
            // more than one "lock", making it an invalid semaphore
            // implementation
            <-limit
        }(w)
    }
    select{}
}