当前位置: 首页 > news >正文

条件变量的使用(golang)

1、背景      

      最近在学习go的一个开源协程池,在源码中有用到锁、信号量,锁相对来说用的是比较多的,信号量相对用的较少,之前研究学习过c++的std::condition_variable,其实和golang的大同小异,个人感觉c++的略强大一些,其wait有两个重载,第二个语法糖的实现主要是为了防止虚假唤醒。

2、介绍

      条件变量的使用场景是,当满足某个条件时,触发某个动作,类似于排队打饭或者挂号看病,当医生或者打饭员准备好后,就触发看病或者打饭的动作。

条件变量有三个函数:

con.Wait()

该函数会将当前协程放入等待队列并阻塞,直到Signal或Broadcast方法将协程从等待队列中移除并唤醒。

con.Signal()

唤醒等待队列中的一个子协程,先唤醒最先阻塞的子协程,被唤醒之后,子协程继续执行。

con.Broadcast()

Broadcast功能类似Signal,不同的是signal只唤醒一个子协程,而broadcast是唤醒所有阻塞的子协程。

3、使用示例

3.1 signal

我们就以在食堂排队打饭为例子,假如本次排队有10位同学排队,这10位同学分别编号为0-9号(就不具体起名字了),当打饭师傅准备好后,就开始排队打饭,示例代码如下:

package mainimport ("fmt""sync""time"
)func DaFan(x int, num *int, wg *sync.WaitGroup, cond *sync.Cond) {defer wg.Done()// 当有一个lock成功后,其他协程阻塞在这里cond.L.Lock()*num++fmt.Println("学号为:", x, "的同学排到了第:", *num, "位")cond.Wait()fmt.Println("学号为:", x, "的同学打饭了")cond.L.Unlock()
}
func main() {var wg sync.WaitGroupnum := 0var lock sync.Mutexcon := sync.NewCond(&lock)for i := 0; i < 10; i++ {wg.Add(1)go DaFan(i, &num, &wg, con)}// 模拟打饭准备time.Sleep(5 * time.Second)// 循环通知这10位同学打饭for i := 0; i < 10; i++ {con.Signal()// 打饭耗时1秒time.Sleep(1 * time.Second)}wg.Wait()
}

上面代码运行结果如下:

3.2 broadcast

broadcast为广播,好比将军一声令下,所有士兵齐刷刷的冲向敌阵,broadcast的应用场景如下:

将军下令,要求全部将士在10s内修整完成,15s后发起总攻,假如我们有十个兵团,分别为1-10号:

package mainimport ("fmt""math/rand""sync""time"
)func GeneralAttack(x int, num *int, wg *sync.WaitGroup, cond *sync.Cond) {defer wg.Done()// 随机一个10秒内的时间用于修整time.Sleep(time.Second * time.Duration(rand.Intn(10)))fmt.Println("兵团", x+1, "在", time.Now().Format("2006-01-02 15:04:05"), "准备完毕,等待总攻")cond.L.Lock()// 准备完毕,等待教练发令cond.Wait()cond.L.Unlock()fmt.Println("兵团", x+1, "开始发起总攻", time.Now().Format("2006-01-02 15:04:05"))
}
func main() {var wg sync.WaitGroupnum := 0var lock sync.Mutexcon := sync.NewCond(&lock)fmt.Println("将军下令,开始修整:", time.Now().Format("2006-01-02 15:04:05"), ",15s后开始总攻")for i := 0; i < 10; i++ {wg.Add(1)go GeneralAttack(i, &num, &wg, con)}// 等待发起总攻time.Sleep(15 * time.Second)fmt.Println("将军下令,开始总攻:", time.Now().Format("2006-01-02 15:04:05"))// 发起总攻con.Broadcast()wg.Wait()
}

运行记录:

liupeng@liupengdeMacBook-Pro test % go run main.go
将军下令,开始修整: 2024-04-06 21:04:32 ,15s后开始总攻
兵团 2 在 2024-04-06 21:04:32 准备完毕,等待总攻
兵团 1 在 2024-04-06 21:04:32 准备完毕,等待总攻
兵团 4 在 2024-04-06 21:04:33 准备完毕,等待总攻
兵团 9 在 2024-04-06 21:04:33 准备完毕,等待总攻
兵团 6 在 2024-04-06 21:04:37 准备完毕,等待总攻
兵团 3 在 2024-04-06 21:04:38 准备完毕,等待总攻
兵团 8 在 2024-04-06 21:04:39 准备完毕,等待总攻
兵团 10 在 2024-04-06 21:04:39 准备完毕,等待总攻
兵团 5 在 2024-04-06 21:04:40 准备完毕,等待总攻
兵团 7 在 2024-04-06 21:04:41 准备完毕,等待总攻
将军下令,开始总攻: 2024-04-06 21:04:47
兵团 6 开始发起总攻 2024-04-06 21:04:47
兵团 7 开始发起总攻 2024-04-06 21:04:47
兵团 2 开始发起总攻 2024-04-06 21:04:47
兵团 3 开始发起总攻 2024-04-06 21:04:47
兵团 8 开始发起总攻 2024-04-06 21:04:47
兵团 10 开始发起总攻 2024-04-06 21:04:47
兵团 5 开始发起总攻 2024-04-06 21:04:47
兵团 1 开始发起总攻 2024-04-06 21:04:47
兵团 9 开始发起总攻 2024-04-06 21:04:47
兵团 4 开始发起总攻 2024-04-06 21:04:47
liupeng@liupengdeMacBook-Pro test % 

如上运行记录,十个兵团在10s内准备完毕,准备完成后等待将军下令发起总攻,每个兵团就是一个协程,并行开始准备,现实情况也是如此,每个兵团准备时间不同,但都在上级要求的时间内准备完成,之后等待将军下令。

使用 条件变量,在调用 Wait() 方法之前,必须要加锁,否则代码会导致程序 panic。原因是调用 Wait 方法,会先把调用者放入等待队列中,然后释放锁。此时如果在未持有锁时调用释放锁的方法,就会导致程序 panic。

相关文章:

  • 计算机网络-TCP重传、滑动窗口、流量控制、拥塞控制
  • 解决element-plus table组件 fixed=“right“(left)浮动后横向滚动文字穿透的问题
  • 【北邮国院大三下】Cybersecurity Law 网络安全法 Week1【更新Topic4, 5】_cyber security la
  • 12.自定义的多帧缓存架构
  • 【PostgreSQL】技术传承:使用Docker快速部署PostgreSQL数据库
  • 游戏引擎中的物理系统
  • 深入探讨string类的奥秘
  • 数据库的简单查询
  • 深入浅出 -- 系统架构之分布式多形态的存储型集群
  • 使用阿里云试用Elasticsearch学习:3.1 处理人类语言——开始处理各种语言
  • 如何进行Python程序的性能优化?
  • 云计算的安全需求
  • 网易雷火 暑期实习提前批一面(48min)
  • 【vite】-【vite介绍】-【vite的基础应用】-【vite的高级应用】-【
  • 【故事】无人机学习之旅
  • ----------
  • 【编码】-360实习笔试编程题(二)-2016.03.29
  • 002-读书笔记-JavaScript高级程序设计 在HTML中使用JavaScript
  • CAP 一致性协议及应用解析
  • django开发-定时任务的使用
  • E-HPC支持多队列管理和自动伸缩
  • extjs4学习之配置
  • HTML5新特性总结
  • HTML-表单
  • IDEA 插件开发入门教程
  • JAVA多线程机制解析-volatilesynchronized
  • KMP算法及优化
  • PHP那些事儿
  • Python - 闭包Closure
  • Vue组件定义
  • windows-nginx-https-本地配置
  • 阿里云爬虫风险管理产品商业化,为云端流量保驾护航
  • 案例分享〡三拾众筹持续交付开发流程支撑创新业务
  • 关于Flux,Vuex,Redux的思考
  • 好的网址,关于.net 4.0 ,vs 2010
  • 前端路由实现-history
  • 前端每日实战:70# 视频演示如何用纯 CSS 创作一只徘徊的果冻怪兽
  • 如何优雅的使用vue+Dcloud(Hbuild)开发混合app
  • 优化 Vue 项目编译文件大小
  • 长三角G60科创走廊智能驾驶产业联盟揭牌成立,近80家企业助力智能驾驶行业发展 ...
  • 带你开发类似Pokemon Go的AR游戏
  • #ifdef 的技巧用法
  • $ is not function   和JQUERY 命名 冲突的解说 Jquer问题 (
  • (1/2) 为了理解 UWP 的启动流程,我从零开始创建了一个 UWP 程序
  • (2)MFC+openGL单文档框架glFrame
  • (3)llvm ir转换过程
  • (pt可视化)利用torch的make_grid进行张量可视化
  • (二)Linux——Linux常用指令
  • (附源码)springboot 校园学生兼职系统 毕业设计 742122
  • (附源码)计算机毕业设计SSM教师教学质量评价系统
  • (一)认识微服务
  • (原創) 未来三学期想要修的课 (日記)
  • (转)scrum常见工具列表
  • .htaccess 强制https 单独排除某个目录
  • .net CHARTING图表控件下载地址