Go 语言在 select 语句中实现优先级¶
本文回顾了Go语言中 select 语句的一些用法,并延伸出了一个如何在 select 中实现优先级的小技巧。
select 语句介绍¶
Go 语言中的 select 语句用于监控并选择一组case语句执行相应的代码。它看起来类似于switch语句,但是select语句中所有case中的表达式都必须是channel的发送或接收操作。一个典型的select使用示例如下:
Go 语言中的 select 关键字也能够让当前 goroutine 同时等待ch1 的可读和ch2的可写,在ch1和ch2状态改变之前,select 会一直阻塞下去,直到其中的一个 channel 转为就绪状态时执行对应case分支的代码。如果多个channel同时就绪的话则随机选择一个case执行。
除了上面展示的典型示例外,接下来我们逐一介绍一些select的特殊示例。
空select¶
空select指的是内部不包含任何case,例如:
空的 select 语句会直接阻塞当前的goroutine,使得该goroutine进入无法被唤醒的永久休眠状态。
只有一个case¶
如果select中只包含一个case,那么该select就变成了一个阻塞的channel读/写操作。
上面的代码,当ch1可读时会执行打印操作,否则就会阻塞。
有 default 语句¶
如果select中还可以包含default语句,用于当其他case都不满足时执行一些默认操作。
上面的代码,当ch1可读时会执行打印操作,否则就执行default语句中的代码,这里就相当于做了一个非阻塞的channel读取操作。
总结¶
select不存在任何的case:永久阻塞当前 goroutineselect只存在一个case:阻塞的发送/接收select存在多个case:随机选择一个满足条件的case执行select存在default,其他case都不满足时:执行default语句中的代码
如何在 select 中实现优先级¶
已知,当select 存在多个 case时会随机选择一个满足条件的case执行。
现在我们有一个需求:我们有一个函数会持续不间断地从ch1和ch2中分别接收任务1和任务2,
如何确保当ch1和ch2同时达到就绪状态时,优先执行任务1,在没有任务1的时候再去执行任务2呢?
高级Go语言程序员小明挠了挠头写出了如下函数:
| Go | |
|---|---|
上面的代码通过嵌套两个select实现了"优先级",看起来是满足题目要求的。但是这代码有点问题,如果ch1和ch2都没有达到就绪状态的话,整个程序不会阻塞而是进入了死循环。
怎么办呢?
小明又挠了挠头,又写下了另一个解决方案:
| Go | |
|---|---|
这一次,小明不仅使用了嵌套的select,还组合使用了for循环和LABEL来实现题目的要求。上面的代码在外层select选中执行job2 := <-ch2时,进入到内层select循环继续尝试执行job1 := <-ch1,当ch1就绪时就会一直执行,否则跳出内层select。
实际应用场景¶
上面的需求虽然是我编的,但是关于在select中实现优先级在实际生产中是有实际应用场景的,例如K8s的controller中就有关于上面这个技巧的实际使用示例,这里在关于select中实现优先级相关代码的关键处都已添加了注释,具体逻辑这里就不展开细说了。
总结¶
本文回顾了Go语言中select语句的一些用法,并延伸出了一个如何在select中实现优先级的小技巧,希望能对大家有所帮助。
最后多嘴一句,Go语言由于自身没有很多奇怪的语法糖和自带代码格式化,相比其他语言来说并不会存在看不懂别人写的代码的情况。所以我们完全可以通过阅读优秀库的源代码,与巨人为伍,与高朋为伴,最终吃上更好的饭。