【问题标题】:Select statement nuances选择语句的细微差别
【发布时间】:2014-09-16 02:38:58
【问题描述】:

我阅读了有关 select 语句及其执行步骤的信息,但我并没有完全理解这里发生了什么。

我创建了两个扇入函数示例(来自Go Concurrency Patterns talk

The first one:

select {
case value := <-g1:
    c <- value
case value := <-g2:
    c <- value
}

按预期从每个通道打印(每个通道都有自己的计数器):

Bob  : 0
Alice: 0
Bob  : 1
Alice: 1
Bob  : 2
Alice: 2
Alice: 3
Alice: 4
Bob  : 3
Alice: 5

The second one:

select {
case c <- <-g1:
case c <- <-g2:
}

它是随机选择一个通道并丢弃另一个通道的值:

Bob  : 0
Alice: 1
Alice: 2
Alice: 3
Bob  : 4
Alice: 5
Bob  : 6
Alice: 7
Alice: 8
Bob  : 9

更新:在写这个问题的时候,我想到了第二个selectwas equal to

var v string
select {
case v = <-g1:
case v = <-g2:
    c <- v
}

但我错了,因为这个总是从第二个通道打印(正如 switch 语句所期望的那样,因为 select 语句中没有失败):

Bob  : 0
Bob  : 1
Bob  : 2
Bob  : 3
Bob  : 4
Bob  : 5
Bob  : 6
Bob  : 7
Bob  : 8
Bob  : 9

有人明白为什么我的第二个示例会创建一个序列吗?

谢谢,

【问题讨论】:

  • 我......实际上不确定那里发生了什么,但我会告诉你你的第三个示例不起作用,因为没有自动失败(在选择语句中也没有失败,句号) .
  • @andlabs,你是对的,谢谢将编辑问题

标签: select concurrency go


【解决方案1】:

您的第二个选择语句被解释为:

v1 := <-g1
v2 := <-g2
select {
case c <- v1:
case c <- v2:
}

As described in the language spec,执行语句时会预先计算每个发送操作符的 RHS:

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

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

因此,在步骤 (1) 中,&lt;-g1&lt;-g2 都将被评估,从每个通道接收值。如果还没有任何东西可以接收,这可能会阻塞。

在 (2) 处,我们等到 c 准备好发送值,然后随机选择 select 语句的一个分支来执行:因为它们都在同一个通道上等待,所以它们都准备好继续。

这解释了您在删除值时看到的行为以及将值发送到 c 的不确定行为。

如果您想等待g1g2,您需要使用您发现的第一个表格。

【讨论】:

  • 谢谢,我想我明白了。令我困惑的一件事是,它一直等到两个通道都准备好发送。 See this last example,Alice 已经准备好,而 Bob 在发送值之前有一个延迟,但不是执行第一个 case select 总是等待 Bob 的延迟(所以这种表示法会产生严重的性能问题,我不'不明白为什么它没有被列为特例 =P)
  • 正如我在回答中提到的,select 语句的实际工作要等到语句的每个分支的 RHS 被评估后才会执行。在决定发送到c 之前,需要从g1g2 接收值。程序不会单独等待可能是表达式一部分的每个发送或接收操作。
【解决方案2】:

根据 [http://golang.org/ref/spec] Go 编程语言规范

for {  // send random sequence of bits to c
    select {
    case c <- 0:  // note: no statement, no fallthrough, no folding of cases
    case c <- 1:
    }
}

它会随机生成 0 或 1。

第二个例子

select {
case c <- <-g1:
case c <- <-g2:
}

当 g1 具有 Bob : 0 而 g2 具有 Alice: 0 时,c &lt;- &lt;-g1c &lt;- &lt;-g2 将执行,但只有一个会执行。

这解释了为什么你有序列0 1 2 3 4 5 6 7 8 9 而不是0 0 1 1 2 2 3 3 4 4

它还说:

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.

根据我的理解,即使c &lt;- &lt;-g1会执行,Alice: 0也会从g2中弹出。所以每次你有Bob : iAlice: i,只会打印出一个。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-04-26
    • 1970-01-01
    • 1970-01-01
    • 2020-12-13
    • 2011-10-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多