选择频道< - < - 频道

时间:2016-02-24 23:28:34

标签: select go channel

我很好奇为什么以下不起作用。通常select default:可以防止死锁,但在这种情况下不会:

package main

import "fmt"

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

    select {
    case a <- <- b:
        fmt.Println("this is impossible")
    default:
        fmt.Println("select worked as naively expected")
    }
}

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

1 个答案:

答案 0 :(得分:6)

a <- <- ba<- (<-b)相同,因为<-运算符可能与最左边的chan相关联。

因此selectcase发送操作(以a<- (something)的形式)。这里发生的是首先评估send语句的右侧表达式(要发送的值) - 即<-b。但是这将永远阻止(因为没有人在b上发送任何东西),所以:

  

致命错误:所有goroutine都睡着了 - 死锁!

相关部分由Spec: Select statements:

组成
  

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

     
      
  1. 对于语句中的所有情况,接收操作的通道操作数以及发送语句的通道和右侧表达式在输入&#34; select& #34;声明。结果是一组要接收或发送的通道,以及要发送的相应值。无论选择哪种(如果有的话)通信操作进行,评估中的任何副作用都将发生。尚未评估具有短变量声明或赋值的RecvStmt左侧的表达式。

  2.   
  3. 如果一个或多个通信可以继续,则可以通过统一的伪随机选择来选择可以继续的单个通信。否则,如果存在默认情况,则选择该情况。如果没有默认情况,&#34;选择&#34;语句阻塞,直到至少有一个通信可以继续。

  4.   
  5. ...

  6.   

因此,如果default存在,select会阻止阻止,如果没有任何通信可以在第2步中继续进行,但您的代码会在步骤中停滞不前1 即可。

只是为了完成,如果会有一个会在b上发送值的goroutine,那么<- b的评估就不会阻止,所以执行{ {1}}不会停留在第2步,您会看到预期的select(因为从"select worked as naively expected"接收仍然无法继续,因此会选择a):

default

Go Playground上尝试。