我很好奇为什么以下不起作用。通常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")
}
}
显然它不喜欢<- <-
,但我想知道这背后发生了什么。在其他情况下允许<- <-
(尽管可能不推荐)。
答案 0 :(得分:6)
a <- <- b
与a<- (<-b)
相同,因为<-
运算符可能与最左边的chan
相关联。
因此select
有case
发送操作(以a<- (something)
的形式)。这里发生的是首先评估send语句的右侧表达式(要发送的值) - 即<-b
。但是这将永远阻止(因为没有人在b
上发送任何东西),所以:
组成致命错误:所有goroutine都睡着了 - 死锁!
执行&#34;选择&#34;声明分几步进行:
对于语句中的所有情况,接收操作的通道操作数以及发送语句的通道和右侧表达式在输入&#34; select& #34;声明。结果是一组要接收或发送的通道,以及要发送的相应值。无论选择哪种(如果有的话)通信操作进行,评估中的任何副作用都将发生。尚未评估具有短变量声明或赋值的RecvStmt左侧的表达式。
如果一个或多个通信可以继续,则可以通过统一的伪随机选择来选择可以继续的单个通信。否则,如果存在默认情况,则选择该情况。如果没有默认情况,&#34;选择&#34;语句阻塞,直到至少有一个通信可以继续。
- 醇>
...
因此,如果default
存在,select
会阻止阻止,如果没有任何通信可以在第2步中继续进行,但您的代码会在步骤中停滞不前1 即可。
只是为了完成,如果会有一个会在b
上发送值的goroutine,那么<- b
的评估就不会阻止,所以执行{ {1}}不会停留在第2步,您会看到预期的select
(因为从"select worked as naively expected"
接收仍然无法继续,因此会选择a
):
default
在Go Playground上尝试。