GO学习-(36) Go语言在select语句中实现优先级
Go语言在select语句中实现优先级
Go语言在select语句中实现优先级
select语句介绍
Go 语言中的 select
语句用于监控并选择一组case
语句执行相应的代码。它看起来类似于switch
语句,但是select
语句中所有case
中的表达式都必须是channel
的发送或接收操作。一个典型的select
使用示例如下:
select {
case <-ch1:
fmt.Println("liwenzhou.com")
case ch2 <- 1:
fmt.Println("q1mi")
}
Go 语言中的 select
关键字也能够让当前 goroutine
同时等待ch1
的可读和ch2
的可写,在ch1
和ch2
状态改变之前,select
会一直阻塞下去,直到其中的一个 channel
转为就绪状态时执行对应case
分支的代码。如果多个channel
同时就绪的话则随机选择一个case
执行。
除了上面展示的典型示例外,接下来我们逐一介绍一些select
的特殊示例。
空select
空select
指的是内部不包含任何case
,例如:
select{
}
空的 select
语句会直接阻塞当前的goroutine
,使得该goroutine
进入无法被唤醒的永久休眠状态。
只有一个case
如果select
中只包含一个case
,那么该select
就变成了一个阻塞的channel
读/写操作。
select {
case <-ch1:
fmt.Println("liwenzhou.com")
}
上面的代码,当ch1
可读时会执行打印操作,否则就会阻塞。
有default语句
如果select
中还可以包含default
语句,用于当其他case
都不满足时执行一些默认操作。
select {
case <-ch1:
fmt.Println("liwenzhou.com")
default:
time.Sleep(time.Second)
}
上面的代码,当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语言程序员小明挠了挠头写出了如下函数:
func worker(ch1, ch2 <-chan int, stopCh chan struct{}) {
for {
select {
case <-stopCh:
return
case job1 := <-ch1:
fmt.Println(job1)
default:
select {
case job2 := <-ch2:
fmt.Println(job2)
default:
}
}
}
}
上面的代码通过嵌套两个select
实现了”优先级”,看起来是满足题目要求的。但是这代码有点问题,如果ch1
和ch2
都没有达到就绪状态的话,整个程序不会阻塞而是进入了死循环。
怎么办呢?
小明又挠了挠头,又写下了另一个解决方案:
func worker2(ch1, ch2 <-chan int, stopCh chan struct{}) {
for {
select {
case <-stopCh:
return
case job1 := <-ch1:
fmt.Println(job1)
case job2 := <-ch2:
priority:
for {
select {
case job1 := <-ch1:
fmt.Println(job1)
default:
break priority
}
}
fmt.Println(job2)
}
}
}
这一次,小明不仅使用了嵌套的select
,还组合使用了for
循环和LABEL
来实现题目的要求。上面的代码在外层select
选中执行job2 := <-ch2
时,进入到内层select
循环继续尝试执行job1 := <-ch1
,当ch1
就绪时就会一直执行,否则跳出内层select
。
实际应用场景
上面的需求虽然是我编的,但是关于在select
中实现优先级在实际生产中是有实际应用场景的,例如K8s的controller中就有关于上面这个技巧的实际使用示例,这里在关于select
中实现优先级相关代码的关键处都已添加了注释,具体逻辑这里就不展开细说了。
// kubernetes/pkg/controller/nodelifecycle/scheduler/taint_manager.go
func (tc *NoExecuteTaintManager) worker(worker int, done func(), stopCh <-chan struct{}) {
defer done()
// 当处理具体事件的时候,我们会希望 Node 的更新操作优先于 Pod 的更新
// 因为 NodeUpdates 与 NoExecuteTaintManager无关应该尽快处理
// -- 我们不希望用户(或系统)等到PodUpdate队列被耗尽后,才开始从受污染的Node中清除pod。
for {
select {
case <-stopCh:
return
case nodeUpdate := <-tc.nodeUpdateChannels[worker]:
tc.handleNodeUpdate(nodeUpdate)
tc.nodeUpdateQueue.Done(nodeUpdate)
case podUpdate := <-tc.podUpdateChannels[worker]:
// 如果我们发现了一个 Pod 需要更新,我么你需要先清空 Node 队列.
priority:
for {
select {
case nodeUpdate := <-tc.nodeUpdateChannels[worker]:
tc.handleNodeUpdate(nodeUpdate)
tc.nodeUpdateQueue.Done(nodeUpdate)
default:
break priority
}
}
// 在 Node 队列清空后我们再处理 podUpdate.
tc.handlePodUpdate(podUpdate)
tc.podUpdateQueue.Done(podUpdate)
}
}
}
最新文章
- angularJS中的ui-router和ng-grid模块
- 使用ffmpeg转换webm格式
- FlipView 索引为0 WP8.1
- Python中文全攻略
- cout输出各种进制
- Ajax与CustomErrors的尴尬
- Canvas贝塞尔三级曲线
- maven工程莫名其妙只在项目名称那里有一个红叉
- 通过PHP前端后台交互/通过ajax前端后台交互/php基础传输数据应用/简单的留言版/简单的注册账户/简单的登录页/
- Git异常情况汇总
- 【freeradius2.x】 安装和学习
- java对文件的基本操作
- Spring AOP实现统一日志输出
- intval — 获取变量的整数值
- zepto 事件分析2($.on)
- java 中的包概念
- PHP/TP5 接口设计中异常处理
- centos 上安装phpstorm
- c 语言中宏定义和定义全局变量的区别
- Guarding the Chessboard(UVa 11214)
热门文章
- Python学习笔记-PuLP库(3)线性规划实例
- 基于MATLAB的手写公式识别(4)
- Property Distribution(DFS)
- 查找和定位Android应用的按钮点击事件的代码位置基于Xposed Hook实现
- UVA11520填充正方形
- MBG:mybatis逆向工程使用
- 一文详解MySQL如何同时自增自减多个字段
- KMP板子(其实还没完全懂...)
- AOP随笔
- [bug] docker: Error response from daemon: Conflict. The container name ";/xx"; is already in use