选择使用通道 <- <- 通道

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

select with channel <- <- channel

问题

我很好奇为什么以下代码不起作用。通常情况下,使用selectdefault可以防止死锁,但在这种情况下却不起作用:

package main

import "fmt"

func main() {
    a := make(chan int)
    b := make(chan int)

    select {
    case a <- <-b:
        fmt.Println("这是不可能的")
    default:
        fmt.Println("select按照预期工作")
    }
}

显然,它不喜欢<- <-这个语法,但我想知道背后发生了什么。在其他情况下,<- <-是允许的(尽管可能不推荐使用)。

英文:

I am curious why the following does not work. In general select with a default: prevents deadlock, but not in this case:

package main

import &quot;fmt&quot;

func main () {
    a := make(chan int)
    b := make(chan int)

    select {
    case a &lt;- &lt;- b:
        fmt.Println(&quot;this is impossible&quot;)
    default:
        fmt.Println(&quot;select worked as naively expected&quot;)
    }
}

Obviously it doesn't like the &lt;- &lt;- but I'm wondering what's going on behind the surface here. In other situations &lt;- &lt;- is allowed (though perhaps not recommended).

答案1

得分: 6

a <- <- ba <- (<-b)是等价的,因为<-运算符与最左边的chan相关联。

所以select语句中有一个带有发送操作的case(形式为a <- (something))。这里发生的情况是,发送语句的右侧表达式(要发送的值)首先被评估,即<-b。但是这将永远阻塞(因为没有人在b上发送任何内容),所以:

fatal error: all goroutines are asleep - deadlock!

相关章节来自规范:Select语句

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

  1. 对于语句中的所有case,接收操作的通道操作数和发送语句的通道和右侧表达式将在进入“select”语句时按照源代码顺序进行一次性评估。结果是一组要从中接收或发送的通道,以及要发送的相应值。在该评估中的任何副作用都将发生,无论选择哪个(如果有)通信操作来继续。带有短变量声明或赋值的RecvStmt左侧的表达式尚未被评估。

  2. 如果一个或多个通信可以继续进行,则通过均匀的伪随机选择选择一个可以继续进行的通信。否则,如果有默认情况,则选择该情况。如果没有默认情况,则“select”语句将阻塞,直到至少有一个通信可以继续进行。

  3. ...

因此,如果存在default,则select步骤2中防止阻塞,如果没有通信可以继续进行,但是您的代码在步骤1中被卡住了。


为了完整起见,如果有一个goroutine在b上发送一个值,那么<- b的评估将不会阻塞,因此select的执行将不会在步骤2中被卡住,并且您将看到预期的"select worked as naively expected"(因为从a接收仍然无法进行,因此将选择default):

go func() { b <- 1 }()

select {
    // ...
}

Go Playground上尝试一下。

英文:

a &lt;- &lt;- b is the same as a&lt;- (&lt;-b), because the &lt;- operator associates with the leftmost chan possible.

So the select has a case with a send operation (in the form of a&lt;- (something)). And what happens here is that the right-hand-side expression of the send statement (the value to be sent) is evaluated first - which is &lt;-b. But this will block forever (because no one is sending anything on b), so:

> fatal error: all goroutines are asleep - deadlock!

Relevant section form the Spec: Select statements:

> 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 if default is present, the select does prevent blocking if none of the communications can proceed in step 2, but your code gets stuck in step 1.


Just to be complete, if there would be a goroutine that would send a value on b, then evaluation of &lt;- b would not block, so the execution of select would not stuck in step 2, and you would see the expected &quot;select worked as naively expected&quot; (because receiving from a could still not proceed therefore default would be chosen):

go func() { b &lt;- 1 }()

select {
    // ...
}

Try it on the Go Playground.

huangapple
  • 本文由 发表于 2016年2月25日 07:28:33
  • 转载请务必保留本文链接:https://go.coder-hub.com/35615326.html
匿名

发表评论

匿名网友

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

确定