go中的select语句和选定的案例

时间:2020-10-15 18:26:42

标签: go

我有一个代码https://play.golang.org/p/y5FGVfXdzC3,试图理解为什么选择第二种情况而不选择第一种情况。

这里是示例:

package main

import (
    "fmt"
    "time"
    "log"
)

func foo1() string {
    log.Println("foo1 evaluated")
    return "quick thing"
}

func foo2(c chan string) {
    time.Sleep(5 * time.Second)
    c <- "sleepy thing"
}

func main() {
    c1 := make(chan string)
    c2 := make(chan string)
    
    go foo2(c2)

    select {
    case c1 <- foo1():
        fmt.Println("received", <-c1)
    case msg := <-c2:
        fmt.Println("received", msg)
    }
}

输出

2009/11/10 23:00:00 foo1 evaluated
received sleepy thing

Program exited.

我希望第一种情况会“更快”,因为我们已经准备好发送给c1了,但是事实并非如此,因此选择了第二种情况。您能帮我了解这种情况吗?

2 个答案:

答案 0 :(得分:1)

将永远不会选择频道c1的情况,因为没有从c1读取goroutine的信息。仅当准备好读取/写入通道时,才会选择该通道。在这种情况下,c2已准备好从中读取,因为有一个goroutine正在等待对其进行写入。如果您从c1创建另一个goroutine读数,则c1c2都将准备就绪,并且将选择其中之一。

将显示消息foo1 evaluated,因为会立即评估大小写(即调用foo1),但是在将值发送到通道之前阻止了大小写。

答案 1 :(得分:1)

the spec(重点是我):

执行“ select”语句的过程分为几个步骤:

  1. 对于该语句中的所有情况,在输入“ select”语句后,接收源的通道操作数以及send语句的通道和右侧表达式的源顺序将精确评估一次。 / strong>。结果是一组要从中接收或发送到的通道,以及要发送的相应值。 该评估将产生任何副作用,而与选择进行哪个(如果有)通信操作无关。带有简短变量声明或赋值的RecvStmt左侧的表达式尚未评估。

因此,您看到foo1 evaluated是因为在选择foo1()之前调用case。这并不意味着选择了第一种情况-实际上,根据输出,将选择第二种情况,否则将不会打印received sleepy thing

无法选择第一种情况,因为c1的发送和接收在同一个goroutine中(运行select的那个),并且没有缓冲。