Cond:条件变量源码解读
Cond条件变量
针对场景
应用于一组goroutine等待某一个条件,当条件为true时唤醒某一个或者所有的goroutine
源码解读
type Cond struct {noCopy noCopyL Locker //当观察(Wait方法)或者修改条件时加锁,Wait方法用锁是保护等待队列的并发安全notify notifyList //等待队列checker copyChecker //运行时检查是否存在复制使用
}// NewCond returns a new Cond with Locker l.
func NewCond(l Locker) *Cond {return &Cond{L: l}
}func (c *Cond) Wait() {c.checker.check() //检查是否存在复制使用t := runtime_notifyListAdd(&c.notify) //把调用的goroutine扔到等待队列c.L.Unlock() //解锁,所以调用Wait前要调用Lock,否则会panicruntime_notifyListWait(&c.notify, t) //阻塞等待(调用gopark)c.L.Lock() //加锁,所以最后一次等待被唤醒后要调用一次UnLock解锁
}func (c *Cond) Signal() {c.checker.check()runtime_notifyListNotifyOne(&c.notify) //唤醒队列头部的goroutine
}func (c *Cond) Broadcast() {c.checker.check()runtime_notifyListNotifyAll(&c.notify) //唤醒队列所有goroutine
}
使用示例
import ("fmt""sync""time"
)func condUseCase() {/*功能:十个运动员、一个裁判,裁判要等待十个运动员都准备就绪后才能开始比赛*/cond := sync.Cond{L: new(sync.Mutex),}ready := 0for i := 0; i < 10; i++ {i := igo func() {time.Sleep(time.Duration(i) * time.Millisecond)cond.L.Lock()ready++cond.L.Unlock()fmt.Printf("运动员%v号准备就绪\n", i)cond.Signal()}()}cond.L.Lock() //Wait方法会调用UnLock,所以要先Lock一下for ready != 10 {cond.Wait()println("裁判被唤醒")}cond.L.Unlock() //条件满足,最后一次Wait唤醒后会调用Lock,所以要UnLock一下println("所有运动员准备就绪")
}
常见踩坑
调用Wait前未加锁
因为Wait内部把当前goroutine扔到等待队列后会调用Unlock解锁,所以调用Wait前要调用Lock加锁,否则会panic
最后一次Wait返回后未解锁
Wait内部被唤醒后会调用Lock方法拿锁准备保护等待队列,但是最后一次Wait被唤醒后发现条件满足就不会操作等待队列后执行Unlock了,所以要在最后一个Wait返回后调用Unlock解锁
调用Wait后,没有检查条件是否满足就继续执行了
Wait方法返回不代表条件已经满足了,需要检查条件是否满足,如果满足继续执行,否则执行Wait继续等待
总结
使用Cond的场景大部分都可以使用Chan代替
Chan代替用法:创建一个没有buffer的chan,等待的goroutine从chan中获取元素阻塞,往chan中传入元素可以唤醒一个goroutine(Signal)、关闭chan可以唤醒所有goroutine(BroadCast)
使用Chan替代上面的示例:
func chanUseCase() {condChan := make(chan struct{})var mu sync.Mutexready := 0for i := 0; i < 10; i++ {i := igo func() {time.Sleep(time.Duration(i) * time.Millisecond)mu.Lock()ready++mu.Unlock()fmt.Printf("运动员%v号准备就绪\n", i)condChan <- struct{}{}}()}for ready != 10 {<-condChanprintln("裁判被唤醒")}println("所有运动员准备就绪")
}
Cond有三个特性Chan无法满足:
- Cond本身具有Locker,不需要创建额外的Locker保证条件的并发修改安全
- Cond可以调用多次BroadCast,但是Chan只能调用一次close
Cond:条件变量源码解读相关推荐
- PostgreSQL 源码解读(32)- 查询语句#17(查询优化-表达式预处理#2)
本节简单介绍了PG查询优化表达式预处理中常量的简化过程.表达式预处理主要的函数主要有preprocess_expression和preprocess_qual_conditions(调用preproc ...
- 源码解读Mybatis List列表In查询实现的注意事项
http://www.blogjava.net/xmatthew/archive/2011/08/31/355879.html 在SQL开发过程中,动态构建In集合条件查询是比较常见的用法,在Myba ...
- Java Review - Queue和Stack 源码解读
文章目录 Pre 概述 Queue Deque ArrayDeque 一览 构造函数 属性 方法 addFirst() addLast() pollFirst() pollLast() peekFir ...
- Spring5源码 - 05 invokeBeanFactoryPostProcessors 源码解读_2
文章目录 Pre 源码解读 总体流程 源码分析 细节解析 [初始化对应的集合 & 遍历用户自己手动添加的后置处理器] [调用实现了PriorityOrdered接口的BeanDefinitio ...
- Ubuntu 16.04下Caffe-SSD的应用(四)——ssd_pascal.py源码解读
前言 caffe-ssd所有的训练时的参数,全部由ssd_pascal.py来定义,之后再去调用相关的脚本和函数,所以想要训练自己的数据,首先要明白ssd_pascal.py各个定义参数的大体意思. ...
- php yii框架源码,yii 源码解读
date: 2017-11-21 18:15:18 title: yii 源码解读 本篇博客阅读指南: php & 代码提示: 工欲善其事必先利其器 yii 源码阅读指南: 整体上全貌上进行了 ...
- aqs java 简书,Java AQS源码解读
1.先聊点别的 说实话,关于AQS的设计理念.实现.使用,我有打算写过一篇技术文章,但是在写完初稿后,发现掌握的还是模模糊糊的,模棱两可. 痛定思痛,脚踏实地重新再来一遍.这次以 Java 8源码为基 ...
- Linux内核网络协议栈:udp数据包发送(源码解读)
<监视和调整Linux网络协议栈:接收数据> <监控和调整Linux网络协议栈的图解指南:接收数据> <Linux网络 - 数据包的接收过程> <Linux网 ...
- python库源码分析_python第三方库Faker源码解读
源码背景 Faker是一个Python第三方库,GITHUB开源项目,主要用于创建伪数据创建的数据包含地理信息类.基础信息类.个人账户信息类.网络基础信息类.浏览器信息类.文件信息类.数字类 文本加密 ...
最新文章
- Spring AOP AspectJ Pointcut Expressions With Examples--转
- 广告片断大收集+穿帮镜头
- redis 基于主从复制的 rce 利用方式
- 机器学习-MNIST数据集-神经网络
- 如何用Python进行大数据挖掘和分析
- 板式橡胶支座弹性模量怎样计算_Midas Civil支座模拟的详细解决方法
- iOS 使用 socket 即时通信(非第三方库)
- 2017.3.16 下午
- clearcase 常用命令
- linux 迅雷 命令行,Linux小迅雷:uGet下载工具加速 | 薄荷开源网
- html测试身高体重,【 身高体重测试】_如何测试_注意事项-大众养生网
- P1567 统计天数
- Springboot无法启动:At least one base package must be specified
- codevs 2867 天平系统3
- 自己写的12306买火车票手机APP
- python获取当前进程pid_Python获取系统所有进程PID及进程名称的方法示例
- 计算机写给未来自己的一段话,现实,致自己 写给自己的霸气一段话汇总73句
- 李宏毅——一天搞懂深度学习PPT学习笔记
- 假作真时真亦假——“真实”IP带来的安全隐患
- oracle数据库的scn,Oracle数据库SCN详解