golang select语句潜在的陷阱

先来看一段代码:

package main

import (
	"fmt"
	"time"
)

func main() {
	select {
	case resp := <-AsyncCall(50):
		fmt.Println(resp)
	case resp := <-AsyncCall(200):
		fmt.Println(resp)
	case resp := <-AsyncCall2(1000):
		fmt.Println(resp)
	}
}

func AsyncCall(t int) <-chan int {
	c := make(chan int, 1)
	go func() {
		time.Sleep(time.Millisecond * time.Duration(t))
		c <- t
	}()
	return c
}

func AsyncCall2(t int) <-chan int {
	c := make(chan int, 1)
	go func() {
		time.Sleep(time.Millisecond * time.Duration(t))
		c <- t
	}()
	// gc or some other reason cost some time
	time.Sleep(time.Duration(200) * time.Millisecond)
	return c
}

这段代码运行的结果会是200和50两种结果随机出现,你会不会感觉到意外呢?

出现这种情况的原因是,当select语句多个需要从chan中读取或写入时,会先轮询一遍所有的case,然后在所有处于就绪(可读/可写)的chan中随机挑选一个进行读取或写入操作,并执行其语句块。如果所有case都未就绪,则执行default语句,如未提供default语句,则当前协程被阻塞。

这段代码里的异步调用AsyncCall2本身的执行的时间为200ms,超过了前两个的任务执行时间。而根据我们之前提到的,select语句在判断chan就绪之前,是会把所有的case语句的判断语句执行一遍的,这就包括了在case语句中的函数调用,因此上边的代码执行逻辑为:AsyncCall(50)—>AsyncCall(200)—>AsyncCall2(1000),然后再等待三者返回的chan就绪,谁先就绪就执行谁;如果三者同时就绪,则随机挑选一个执行;没有就绪就阻塞直到有一个就绪。显然当AsyncCall2(1000)执行完成时,前两个异步任务已经完成,因此在判断chan就绪时就是随机选择前两个chan了,故而打印结果为50和200随机出现。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值