2016-02-24 47 views
5

我很好奇为什么以下不起作用。一般来说selectdefault:防止僵局,但不是在这种情况下:选择与通道< - < - 通道

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") 
    } 
} 

显然它不喜欢的<- <-但我想知道这是怎么回事的表面背后。在其他情况下<- <-被允许(尽管可能不推荐)。

回答

6

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

所以select有一个case与发送操作(的形式a<- (something))。这里发生的是send语句的右侧表达式(要发送的值)首先被计算 - 这是<-b。但是,这将永远阻止(因为没有人在b发送任何数据),所以:

fatal error: all goroutines are asleep - deadlock!

相关部分形成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. ...

所以,如果default存在,则select确实防止阻塞,如果没有的通信可以在步骤2中继续,但是您的代码卡在步骤1中。


只要是完整的,是否会有这将在b发送的值,那么<- b评价不会阻止的goroutine,所以select的执行将不会停留在步骤2中,和你会看到预期的"select worked as naively expected"(因为从a接收仍然无法因此继续default将选择):

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

select { 
    // ... 
} 

尝试它的Go Playground