简体   繁体   English

选择频道< - < - 频道

[英]select with channel <- <- channel

I am curious why the following does not work. 我很好奇为什么以下不起作用。 In general select with a default: prevents deadlock, but not in this case: 通常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")
    }
}

Obviously it doesn't like the <- <- but I'm wondering what's going on behind the surface here. 显然它不喜欢<- <-但是我想知道这背后发生了什么。 In other situations <- <- is allowed (though perhaps not recommended). 在其他情况下允许<- <- (尽管可能不推荐)。

a <- <- b is the same as a<- (<-b) , because the <- operator associates with the leftmost chan possible. a <- <- ba<- (<-b) ,因为<-运算符可能与最左边的chan关联。

So the select has a case with a send operation (in the form of a<- (something) ). 所以selectcase与发送操作(在形式a<- (something) )。 And what happens here is that the right-hand-side expression of the send statement (the value to be sent) is evaluated first - which is <-b . 这里发生的是首先评估send语句的右侧表达式(要发送的值) - 这是<-b But this will block forever (because no one is sending anything on b ), so: 但这将永远阻止(因为没有人在b上发送任何东西),所以:

fatal error: all goroutines are asleep - deadlock! 致命错误:所有goroutines都睡着了 - 僵局!

Relevant section form the Spec: Select statements: Spec:Select语句的相关部分

Execution of a "select" statement proceeds in several steps: 执行“select”语句分几步进行:

  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. 对于语句中的所有情况,接收操作的通道操作数以及发送语句的通道和右侧表达式在输入“select”语句后按源顺序精确计算一次。 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. 尚未评估具有短变量声明或赋值的RecvStmt左侧的表达式。

  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. 如果没有默认情况,则“select”语句将阻塞,直到至少一个通信可以继续。

  3. ... ...

So if default is present, the select does prevent blocking if none of the communications can proceed in step 2 , but your code gets stuck in step 1 . 因此,如果存在default ,则如果在步骤2中没有任何通信可以继续,则select确实会阻止阻塞,但是您的代码在步骤1中卡住了。


Just to be complete, if there would be a goroutine that would send a value on b , then evaluation of <- b would not block, so the execution of select would not stuck in step 2, and you would see the expected "select worked as naively expected" (because receiving from a could still not proceed therefore default would be chosen): 只是要完整,如果有一个goroutine会在b上发送一个值,那么<- b评估就不会阻塞,所以select的执行不会停留在第2步,你会看到预期的"select worked as naively expected" (因为从a接收仍然无法继续,因此将选择default ):

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

select {
    // ...
}

Try it on the Go Playground . Go Playground尝试一下。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM