【问题标题】:Multiple receivers on a single channel. Who gets the data?单个通道上的多个接收器。谁得到数据?
【发布时间】:2015-10-10 22:50:08
【问题描述】:

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

【问题讨论】:

    标签: go blocking channel


    【解决方案1】:

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

    【讨论】:

      【解决方案2】:

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

      见语言spec

      “选择”语句的执行分几个步骤进行:

      1. 对于语句中的所有情况,接收操作的通道操作数以及发送的通道和右侧表达式 语句在输入时按源顺序只计算一次 “选择”语句。结果是一组要接收的通道 from 或 send to,以及要发送的相应值。任何一方 无论哪种情况(如果有),该评估中的影响都会发生 选择通讯操作继续。上的表达 带有短变量声明的 RecvStmt 的左侧或 作业尚未评估。
      2. 如果一个或多个通信可以继续,则通过统一的伪随机选择选择一个可以继续的通信。 否则,如果存在默认情况,则选择该情况。如果有 不是默认情况,“select”语句阻塞,直到至少一个 的通信可以继续进行。
      3. 除非所选案例是默认案例,否则会执行相应的通信操作。
      4. 如果所选用例是带有短变量声明或赋值的 RecvStmt,则左侧表达式为 评估并分配接收到的一个(或多个)值。
      5. 执行所选案例的语句列表。

      【讨论】:

      • 比赛条件?这样做安全吗?
      • 是的,当然,哪个 goroutine 获取元素很有趣。
      • 我不会称之为“竞争条件”。该术语通常被定义为表示由于对变量的不安全并发访问而导致的意外行为。通道被设计为可以安全地并发访问,在一个无缓冲的通道上多个阅读器没有任何问题。
      • 我认为您根本不需要该术语,添加它只会混淆问题。它本身并没有什么“活泼”,因为通道的内部结构序列化了接收操作。
      • 在 CSP 术语中,这是一个不确定的选择。这与比赛条件不同。正如 JimB 所说,后者是偶然且有害的。如果您希望选择具有确定性,您可能需要为 N 个接收 goroutine 使用 N 个通道而不是 1 个。
      【解决方案3】:

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

      所以通道发送/接收操作阻塞,直到对方准备好:

      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)
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2023-02-22
        • 2016-03-02
        • 2021-01-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多