Go开发 Channel彻底研究之Select选择规则

开发 前端
在执行select语句时,运行时系统会自上而下判断每个case中的发送或接收操作是否可以被立即执行,这里的立即执行的意思是当前goroutine不会因此操作而阻塞。

从左往右,从上往下

对于select的求值,一条case中,从左往右求值;多条case,从上往下,下面举几个例子说明:

var ch2 chan int
var ch4 chan int
var chs = []chan int{ch2, ch4}
var numbers = []int{1, 2, 3, 4, 5}
func main() {

select {
case getChan(0) <- getNumber(2):
fmt.Println("1th case is selected")
case getChan(1) <- getNumber(3):
fmt.Println("2th case is selected")
default:
fmt.Println("default!")
}
}
func getChan(i int) chan int {
fmt.Printf("chs[%d]\n",i);
return chs[i]
}
func getNumber(i int) int {
fmt.Printf("numbers[%d]\n",i)
return numbers[i]
}

代码分析

这段代码设计的也比较巧妙,调试看出getChan先于getNumber执行,说明一条case中是从左往右求值的;同理,多条case是从上往下执行。

但是有一个问题,我们发现ch2和ch4都是nil,简化形式如下:

//ch == nil
var ch chan int

select {
case ch <- 0:
default:
fmt.Println("默认执行...")
}

select的case中允许这样写,但相当于没有写,永远不会执行。除此外,select中还有很多奇怪的使用方式,再比如:

//无缓冲通道
ch := make(chan int)

select {
case ch <- 0:
default:
fmt.Println("默认执行...")
}

没有接收方代码,但是依然也不会错。

具体运行规则

一、在执行select语句时,运行时系统会自上而下判断每个case中的发送或接收操作是否可以被立即执行,这里的立即执行的意思是当前goroutine不会因此操作而阻塞。

要点:case中的语句要能立即执行

二、当发现第一个满足条件的case时,运行时系统就会执行该case所包含的语句,同时,其它case也会被忽略。

要点:只执行一个case

三、如果同时有多个case满足条件,那么运行时会通过一个伪随机的算法决定哪一个case将会执行。

要点:多个case同时满足,使用算法选择一个

chanCap := 5
ch := make(chan int, chanCap)

for i := 0; i < chanCap; i++ {
select {
case ch <- 1:
case ch <- 2:
case ch <- 3:
}
}

for i := 0; i < chanCap; i++ {
fmt.Printf("%v ", <-ch)
}

代码分析

这段代码也很巧妙,验证了多个case同时满足条件时,如何进行随机选择。

输出:

3 2 2 2 3
2 1 1 1 2
1 1 3 2 1

可以看出,作者电脑上每次显示的都是一些随机选择。

四、如果所有的case都不能立即执行,且没有default,那么select会阻塞,直到某个接收或发送的case操作能立即执行。

ch1 := make(chan int)
ch2 := make(chan int)
go func() {
time.Sleep(1 * time.Second)
ch1 <- 10
}()
go func() {
time.Sleep(1 * time.Second)
ch1 <- 20
}()
select {
case d := <-ch1:
fmt.Println(d)
case d := <-ch2:
fmt.Println(d)
}

代码分析

select等待两个channel,由于都延时且没有default,所以select阻塞等待。1s后ch1可以立即执行,因此打印10,select也退出选择。

五、如果所有通道都是nil且没有default,且会发生死锁

// ch1 == nil
var ch1 chan int
select {
case <-ch1:
fmt.Println("hello")
// 没有default
}

select和for的配合

select和for的搭配有很多问题,本篇只说明一个问题:

select的case中可以使用break,但是只跳出select(区域1),如果想跳出for循环(区域2),需要一些辅助技巧。

总结

介绍了select的一些基本选择规则,也是需要背诵的知识点。

责任编辑:武晓燕 来源: 今日头条
相关推荐

2023-03-09 09:06:13

ChanneGo开发

2023-05-29 09:25:38

GolangSelect

2023-07-13 08:06:05

应用协程阻塞

2021-10-11 11:58:41

Channel原理recvq

2021-10-09 19:05:06

channelGo原理

2017-04-11 14:45:22

机器学习学习平台大数据平台

2020-12-27 10:15:44

Go语言channel管道

2021-09-01 18:38:59

Goselectdefault

2023-07-27 13:46:10

go开源项目

2022-02-22 08:55:29

SelectPoll/ Epoll

2011-08-12 09:35:05

javascript

2019-12-10 13:55:10

Go指针存储

2020-06-22 07:18:21

Java语言开发

2019-10-17 09:00:00

GoRust编程语言

2023-11-03 18:03:54

Web应用Python

2021-09-30 09:21:28

Go语言并发编程

2023-07-19 08:01:04

switch​select​语句

2023-12-20 07:30:54

Goselect编程

2022-03-04 10:07:45

Go语言字节池

2023-06-15 08:06:55

gogolang通信
点赞
收藏

51CTO技术栈公众号