选择频道< - < - 频道

Diz*_*Sma 5 select channel go

我很好奇为什么以下不起作用.通常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")
    }
}
Run Code Online (Sandbox Code Playgroud)

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

icz*_*cza 6

a <- <- b是相同的a<- (<-b),因为<-操作员与最左边的人联系在一起chan.

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

致命错误:所有goroutines都睡着了 - 僵局!

Spec:Select语句的相关部分:

执行"select"语句分几步进行:

  1. 对于语句中的所有情况,接收操作的通道操作数以及发送语句的通道和右侧表达式在输入"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 {
    // ...
}
Run Code Online (Sandbox Code Playgroud)

在Go Playground尝试一下.