我很好奇为什么以下不起作用.通常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)
显然它不喜欢,<- <-但我想知道这背后发生了什么.在其他情况下<- <-是允许的(尽管可能不推荐).
a <- <- b是相同的a<- (<-b),因为<-操作员与最左边的人联系在一起chan.
所以select有一个case带有发送操作(以形式a<- (something)).这里发生的是首先评估send语句的右侧表达式(要发送的值) - 即<-b.但这将永远阻止(因为没有人发送任何东西b),所以:
致命错误:所有goroutines都睡着了 - 僵局!
Spec:Select语句的相关部分:
执行"select"语句分几步进行:
对于语句中的所有情况,接收操作的通道操作数以及发送语句的通道和右侧表达式在输入"select"语句后按源顺序精确计算一次.结果是一组要接收或发送的通道,以及要发送的相应值.无论选择哪种(如果有的话)通信操作进行,评估中的任何副作用都将发生.尚未评估具有短变量声明或赋值的RecvStmt左侧的表达式.
如果一个或多个通信可以继续,则可以通过统一的伪随机选择来选择可以继续的单个通信.否则,如果存在默认情况,则选择该情况.如果没有默认情况,则"select"语句将阻塞,直到至少一个通信可以继续.
...
因此,如果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尝试一下.