golang的select反直觉逻辑

29 阅读1分钟
package main

import (
	"log"
	"time"
)

var intBufChan = make(chan int, 2)

func main() {
	select {
	case <-time.After(time.Second):
		log.Printf("select case 1\n")
	case getIntBufChan() <- getInt(2):
		log.Printf("select case 2\n")
	}
}

func getInt(i int) int {
	log.Printf("get int sleep 2s...\n")
	time.Sleep(2 * time.Second)
	log.Printf("get int %d\n", i)
	return i
}

func getIntBufChan() chan int {
	log.Printf("get int buf chan\n")
	return intBufChan
}

在上面的代码中,是一定会打印出select case 1吗?不一定,其实是两个case都有可能执行,原因是在select的在对chan进行通信操作之前,会对case的表达式进行一次求值运算,其结果将作为接收或发送操作的通道,以及相应待发送的值。 所以这段代码的执行顺序:

第一种结果选择 case 1,执行顺序为:time.After(time.Second) → getIntBufChan() → getInt(2) → time.Sleep(2 * time.Second) → fmt.Printf("select case 1\n")

第二种结果选择 case 2,执行顺序为:time.After(time.Second) → getIntBufChan() → getInt(2) → time.Sleep(2 * time.Second) → fmt.Printf("select case 2\n")

不管怎么样,都要等getInt()函数执行完后,select才会开始对通道的通信进行操作!而getInt()函数执行完,两个case的通道都是ready的,所以结果是随机的。

参考:ciphersaw.me/2022/11/09/…