为什么在该通道上选择了goroutine却阻止了写通道?

时间:2019-05-22 05:39:53

标签: go

我对以下代码感到困惑,我在代码中写下了一些注释,指出了我的困惑。在代码末尾有执行的结果,我还写下了我期望的结果。

package main

import (
    "fmt"
    "time"
)

func sendRPC() bool {
    time.Sleep(5 * time.Second)
    return true
}

func main() {
    done := make(chan struct{})
    ch := make(chan bool)

    go func() { // goroutine A
        select {
        case ch <- sendRPC():
            fmt.Println("RPC return")
        case <-done:
            fmt.Println("exit")
        }
    }()

    select {
    case <-ch:
    case <-time.After(1000 * time.Millisecond):
        fmt.Println("timeout")
        if len(done) == 0 {
            fmt.Println("1")
            // here write done channel will block until sendRPC() return, why?
            // I expect that the write is nonblock because goroutine A is select on done channel.
            done <- struct{}{}
            fmt.Println("2")
        }
    }

    // result:
    // timeout (after about 1 second)
    // 1
    // exit    (after about 5 seconds, I expect that it is printed after about 1 second too.)
    // 2

}

1 个答案:

答案 0 :(得分:1)

specification says

  

对于该语句中的所有情况,输入“ select”语句后,将按源顺序对接收操作的通道操作数以及send语句的通道表达式和右侧表达式进行一次精确评估。结果是一组要从中接收或发送到的通道,以及要发送的相应值。不管选择进行哪个通信操作,都会发生该评估中的任何副作用。

goroutine A中的select通道集等待对sendRPC()的求值。看看这个等效的goroutine可能会有所帮助:

go func() { // goroutine A
    v := sendRPC()  // waits for 5 seconds
    select {
    case ch <- v:
        fmt.Println("RPC return")
    case <-done:
        fmt.Println("exit")
    }
}()

done上的接收被延迟5秒。