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的可写,在ch1ch2状态改变之前,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读取操作。

总结

  1. select 不存在任何的 case:永久阻塞当前 goroutine
  2. select 只存在一个 case:阻塞的发送/接收
  3. select 存在多个 case:随机选择一个满足条件的case执行
  4. select 存在 default,其他case都不满足时:执行default语句中的代码

如何在select中实现优先级

已知,当select 存在多个 case时会随机选择一个满足条件的case执行。

现在我们有一个需求:我们有一个函数会持续不间断地从ch1ch2中分别接收任务1和任务2,

如何确保当ch1ch2同时达到就绪状态时,优先执行任务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实现了”优先级”,看起来是满足题目要求的。但是这代码有点问题,如果ch1ch2都没有达到就绪状态的话,整个程序不会阻塞而是进入了死循环。

怎么办呢?

小明又挠了挠头,又写下了另一个解决方案:

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)
}
}
}

GO学习-(36) Go语言在select语句中实现优先级的更多相关文章

  1. 【MYSQL】update/delete/select语句中的子查询

    update或delete语句里含有子查询时,子查询里的表不能在update或是delete语句中,如含有运行时会报错:但select语句里含有子查询时,子查询里的表可以在select语句中. 如:把 ...

  2. SQL SERVER SELECT语句中加锁选项的详细说明 [转]

    SQL Server提供了强大而完备的锁机制来帮助实现数据库系统的并发性和高性能.用户既能使用SQL Server的缺省设置也可以在select 语句中使用“加锁选项”来实现预期的效果. 本文介绍了S ...

  3. select语句中会影响查询效率的因素

    1.没有创建索引,或者没有正确使用索引;2.存在死锁的情况,从而导致select语句挂起; 3.返回不必要的列,如很多人喜欢在程序中使用select * from 这样会查询表或视图中的所有字段,如果 ...

  4. SQL SERVER SELECT语句中加锁选项的详细说明

    共享锁(读锁)和排他锁(写锁)   共享锁(S锁):共享 (S) 用于不更改或不更新数据的操作(只读操作),如 SELECT 语句. 如果事务T对数据A加上共享锁后,则其他事务只能对A再加共享锁,不能 ...

  5. 关于Mysql select语句中拼接字符串的记录

    在mysql的SELECT语句中拼接两列(或多列)的字符串显示: mysql> select concat(dname,loc) from dept; 以上语句便把dept表的dname,loc ...

  6. MySQL 语句中执行优先级——and比or高

    转: MySQL 语句中执行优先级——and比or高 2017年04月20日 13:33:03 十步行 阅读数:7381   版权声明:本文为博主原创文章,未经博主允许不得转载. https://bl ...

  7. 【SQL学习笔记】一、select语句

    SQL有别于其他的编程语言的一点在于首先处理的并不是写在第一行的语句(select),而是from字句. 为了更详细的了解select语句的每个部分,举例如下: 该语句返回的结果是下订单超过4次的女顾 ...

  8. oracle(3)select语句中常用的关键字说明

    1.select 查询表中的数据 select * from stu: ---查询stu表所有的数据,*代表所有2.dual ,伪表,要查询的数据不存在任何表中时使用 select sysdate f ...

  9. SELECT语句中的for update的用法(锁的运用)

    回复1:一般FOR UPDATE用在PL/SQL的游标里,它的作用就是一个行级锁(对游标里所有的记录),如果其他进程要更新这个游标行级锁里的记录,就必须等待当前进程的COMMIT或者回滚. 该语句用来 ...

随机推荐

  1. MySQL数据库干货分享!unsigned使用案例分析

    今天主要写一下unsigned的使用,进入正文前先分享一套博主觉得讲的很详细很实用的MySQL教程给大家 https://www.bilibili.com/video/BV1fx411X7BD 好了, ...

  2. day-26-封装-property装饰器-反射

    一.super进阶 在多继承中:严格按照mro顺序来执行 super是按照mro顺序来寻找当前类的下一类 在py3中不需要传参数,自动就帮我们寻找当前类的mro顺序的下一个类中的同名方法 在py2中的 ...

  3. [源码分析] 并行分布式任务队列 Celery 之 Timer & Heartbeat

    [源码分析] 并行分布式任务队列 Celery 之 Timer & Heartbeat 目录 [源码分析] 并行分布式任务队列 Celery 之 Timer & Heartbeat 0 ...

  4. 洛谷P1089 津津的储蓄计划

    题目描述 津津的零花钱一直都是自己管理.每个月的月初妈妈给津津300元钱,津津会预算这个月的花销,并且总能做到实际花销和预算的相同. 为了让津津学习如何储蓄,妈妈提出,津津可以随时把整百的钱存在她那里 ...

  5. The 2014 ACM-ICPC Asia Mudanjiang Regional First Round A

    网选A,水题: 这个是水题,只要枚举一遍,看有多少a[i-1]<a[i]>a[i+1],不解释了.

  6. Linux文件共享服务之Samba

    目录 Samba Samba的配置 Samba Samba是一个能让Linux系统应用Microsoft网络通讯协议的软件,而SMB是Server Message Block的缩写,即为服务器消息块 ...

  7. hdu4940 有上下界的无源可行流判断

    题意:       给你一个强连通图,然后问你是否可以找到任意满足条件的集合S,S是非空集合,T是S的补集,满足sum(D[i ,j]) <= sum(D[j,i] + B[j,i]) i属于S ...

  8. POJ1703带权并查集(距离或者异或)

    题意:       有两个黑社会帮派,有n个人,他们肯定属于两个帮派中的一个,然后有两种操作 1 D a b 给出a b 两个人不属于同一个帮派 2 A a b 问a b 两个人关系 输出 同一个帮派 ...

  9. URL分发器(视图层)

    目录 视图 URL映射 path函数 URL中传入参数 普通传入参数 变量形式传入参数 ​URL中包含另一个urls模块 URL命名.URL反转.应用命名空间 视图 视图一般都写在 app  的 vi ...

  10. 一起来刷《剑指Offer》-- 题目一:找出数组中重复的数字(Python多种方法实现)

    数组中重复的数字 最近在复习算法和数据结构(基于Python实现),然后看了Python的各种"序列"--比如列表List.元组Tuple和字符串String,后期会写一篇博客介绍 ...