选择语句的细微差别

huangapple go评论79阅读模式
英文:

Select statement nuances

问题

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

我创建了两个Fan-In函数的示例(来自Go并发模式演讲)。

第一个示例

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

第二个示例

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与以下代码等效(http://play.golang.org/p/-QCr7bEF40):

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

但我错了,因为这个示例总是从第二个通道打印输出(与switch语句的预期行为相同,因为在select语句中没有fallthrough):

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

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

谢谢,

英文:

I read about select statements and its execution steps but I'm not fully understanding what's happening here.

I created two examples of a Fan-In function (from the Go Concurrency Patterns talk)

The first one:

select {
case value := &lt;-g1:
	c &lt;- value
case value := &lt;-g2:
	c &lt;- value
}

Prints from each channel as expected (each channel keeps its own counter):

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 &lt;- &lt;-g1:
case c &lt;- &lt;-g2:
}

It is randomly selecting a channel and discarding the other one's value:

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

Update: while writing this question, I thought the second select was equal to:

var v string
select {
case v = &lt;-g1:
case v = &lt;-g2:
	c &lt;- v
}

But I was wrong, because this one always prints from the second channel (<strike>as expected from a switch like statement</strike> because there isn't fallthrough in select statements):

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

Does someone understand why my second example creates a sequence?

Thanks,

答案1

得分: 4

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

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

根据语言规范的描述,每个发送操作符的右侧表达式在执行语句时会被提前评估:

"select" 语句的执行分为几个步骤:

  1. 对于语句中的所有 case,接收操作的通道操作数以及发送语句的通道和右侧表达式将按照源代码顺序进行一次性评估。结果是一组要接收或发送的通道,以及要发送的相应值。在此评估中的任何副作用都将发生,无论选择哪个(如果有)通信操作来继续。带有短变量声明或赋值的 RecvStmt 的左侧表达式尚未评估。
  2. 如果有一个或多个通信可以继续,将通过均匀伪随机选择选择一个可以继续的通信。否则,如果有一个默认 case,将选择该 case。如果没有默认 case,则 "select" 语句将阻塞,直到至少有一个通信可以继续。
  3. ...

因此,在步骤(1)中,<-g1<-g2 都将被评估,从每个通道接收值。如果还没有要接收的值,这可能会阻塞。

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

这解释了你观察到的值被丢弃,并且在向 c 发送值时出现了非确定性行为。

如果你想要等待 g1g2,你需要使用你已经发现的第一种形式的语句。

英文:

Your second select statement is interpreted as:

v1 := &lt;-g1
v2 := &lt;-g2
select {
case c &lt;- v1:
case c &lt;- v2:
}

As described in the language spec, the RHS of each send operator will be evaluated up front when the statement is executed:

> 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. ...

So as step (1) both &lt;-g1 and &lt;-g2 will be evaluated, receiving values from each channel. This may block if there is nothing to receive yet.

At (2), we wait until c is ready to send a value and then randomly choose a branch of the select statement to execute: since they are both waiting on the same channel, they are both ready to proceed.

This explains the behaviour you saw where values are dropped and you got non-deterministic behaviour in which value was sent to c.

If you want to wait on g1 and g2, you will need to use the first form for your statement as you've discovered.

答案2

得分: 1

根据[http://golang.org/ref/spec]《Go编程语言规范》的说明:

第一个例子中,通过for循环向c发送随机的0和1的序列:

for {
    select {
    case c <- 0:  // 注意:没有语句,没有穿透,没有合并的case
    case c <- 1:
    }
}

它会随机生成0或1。

对于第二个例子:

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

g1的值为Bob: 0g2的值为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

规范还指出:

“按照源代码的顺序,在进入select语句时确定。结果是一组要接收或发送的通道,以及要发送的相应值。”

根据我的理解,即使执行了c <- <-g1Alice: 0也会从g2中弹出。所以每次你都会得到Bob: iAlice: i中的一个,只会打印出其中一个。

英文:

According to [http://golang.org/ref/spec] The Go Programming Language Specification

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

It'll generate 0 or 1 randomly.

For the second exaple

select {
case c &lt;- &lt;-g1:
case c &lt;- &lt;-g2:
}

when g1 has Bob : 0 and g2 has Alice: 0, either c &lt;- &lt;-g1 or c &lt;- &lt;-g2 will execute , but only one will.

That explain why you have the sequence 0 1 2 3 4 5 6 7 8 9 but not 0 0 1 1 2 2 3 3 4 4

And it also say:

in source order, upon entering the &quot;select&quot; statement. The result is a set of channels to receive from or send to, and the corresponding values to send.

According to my comprehension , even if c &lt;- &lt;-g1 will execute , Alice: 0 also will pop up from g2. So every time you have Bob : i and Alice: i, only one will be printed out.

huangapple
  • 本文由 发表于 2014年9月16日 10:38:58
  • 转载请务必保留本文链接:https://go.coder-hub.com/25859867.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定