选择语句细微差别

时间:2014-09-16 02:38:58

标签: select concurrency go

我读到了关于select语句及其执行步骤,但我还没有完全理解这里发生了什么。

我创建了两个Fan-In函数示例(来自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

更新:在撰写此问题时,我想到了第二个select was equal to

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

但我错了,因为这个总是从第二个频道打印(正如预期的那样,从语句开关,因为select语句没有落后):

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

是否有人理解为什么我的第二个示例会创建序列?

谢谢,

2 个答案:

答案 0 :(得分:4)

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

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

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

  

执行&#34;选择&#34;声明分几步进行:

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

因此,步骤(1)将评估<-g1<-g2,从每个频道接收值。如果还没有什么东西可以接收,这可能会阻止。

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

这解释了您看到的值被删除的行为,并且您获得了将值发送到c的非确定性行为。

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

答案 1 :(得分:1)

根据[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 <- <-g1c <- <-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 <- <-g1将执行,Alice: 0也会从g2弹出。因此,每次有Bob : iAlice: i时,只会打印出一个。