Go标准库的Cond

Go 标准库提供 Cond 原语的目的是,为等待 / 通知场景下的并发问题提供支持。Cond 通常应用于等待某个条件的一组 goroutine,等条件变为 true 的时候,其中一个 goroutine 或者所有的 goroutine 都会被唤醒执行。

Cond 是和某个条件相关,这个条件需要一组 goroutine 协作共同完成,在条件还没有满足的时候,所有等待这个条件的 goroutine 都会被阻塞住,只有这一组 goroutine 通过协作达到了这个条件,等待的 goroutine 才可能继续进行下去。

那这里等待的条件是什么呢?等待的条件,可以是某个变量达到了某个阈值或者某个时间点,也可以是一组变量分别都达到了某个阈值,还可以是某个对象的状态满足了特定的条件。总结来讲,等待的条件是一种可以用来计算结果是 true 还是 false 的条件。

Cond的基本用法

标准库中的 Cond 并发原语初始化的时候,需要关联一个 Locker 接口的实例,一般我们使用 Mutex 或者 RWMutex。

Cond 关联的 Locker 实例可以通过 c.L 访问,它内部维护着一个先入先出的等待队列。

我们分别看下它的三个方法Broadcast、Signal和Wait方法。

  • Signal

允许调用者Caller唤醒一个等待此Cond的goroutine,如果此时没有等待的goroutine,则无需通知waiter,如果Cond等待队列中有一个或多个等待的goroutine,则需要从等待队列中移除第一个goroutine并把它唤醒。

  • Broadcast

允许调用者Caller唤醒所有等待此Cond的goroutine,如果此时没有等待的goroutine,显然无需通知waiter,如果Cond等待队列中有一个或多个等待的goroutine,则清空所有等待的goroutine,并全部唤醒。

  • Wait

会把调用者Caller放入Cond的等待队列中并阻塞,直到被Signal或者Broadcast方法从等待队列中移除并唤醒。

注意:调用Signal和Broadcast方法,不强求持有c.L的锁,调用Wait方法是必须要持有c.L的锁。

示例

10 个运动员进入赛场之后需要先做拉伸活动活动筋骨 ,在自己的赛道上做好准备;等所有的运动员都准备好之后,裁判员才会打响发令枪。

每个运动员做好准备之后,将 ready 加一,表明自己做好准备了,同时调用 Broadcast 方法通知裁判员。因为裁判员只有一个,所以这里可以直接替换成 Signal 方法调用。

调用 Broadcast 方法的时候,我们并没有请求 c.L 锁,只是在更改等待变量的时候才使用到了锁。

裁判员会等待运动员都准备好。虽然每个运动员准备好之后都唤醒了裁判员,但是裁判员被唤醒之后需要检查等待条件是否满足(运动员都准备好了)。可以看到,裁判员被唤醒之后一定要检查等待条件,如果条件不满足还是要继续等待。

func main() {
c := sync.NewCond(&sync.Mutex{})
ready := 0 for i := 0; i < 10; i ++{
go func(i int) {
time.Sleep(time.Second * time.Duration(rand.Int63n(10)))
// 加锁更改等待条件
c.L.Lock()
ready++
c.L.Unlock() fmt.Printf("运动员%d已准备就绪\n",i)
// 广播唤醒等待者,这里可以使用Broadcast和Signal
c.Signal()
}(i)
} c.L.Lock()
for ready != 10 {
c.Wait()
log.Println("裁判员被唤醒一次")
} c.L.Unlock() log.Println("所有运动员都准备就绪,比赛开始。。。")
}

Cond实现原理


type Cond struct {
noCopy noCopy // 当观察或者修改等待条件的时候需要加锁
L Locker // 等待队列
notify notifyList
checker copyChecker
} func NewCond(l Locker) *Cond {
return &Cond{L: l}
} func (c *Cond) Wait() {
c.checker.check()
// 增加到等待队列中
t := runtime_notifyListAdd(&c.notify)
c.L.Unlock()
// 阻塞休眠直到被唤醒
runtime_notifyListWait(&c.notify, t)
c.L.Lock()
} func (c *Cond) Signal() {
c.checker.check()
runtime_notifyListNotifyOne(&c.notify)
} func (c *Cond) Broadcast() {
c.checker.check()
runtime_notifyListNotifyAll(&c.notify)
}

runtime_notifyListXXX 是运行时实现的方法,实现了一个等待 / 通知的队列。

copyChecker 是一个辅助结构,可以在运行时检查 Cond 是否被复制使用。

Signal 和 Broadcast 只涉及到 notifyList 数据结构,不涉及到锁。

Wait 把调用者加入到等待队列时会释放锁,在被唤醒之后还会请求锁。在阻塞休眠期间,调用者是不持有锁的,这样能让其他 goroutine 有机会检查或者更新等待变量。

Go标准库Cond的更多相关文章

  1. Python标准库--typing

    作者:zhbzz2007 出处:http://www.cnblogs.com/zhbzz2007 欢迎转载,也请保留这段声明.谢谢! 1 模块简介 Python 3.5 增加了一个有意思的库--typ ...

  2. PHP SPL(PHP 标准库)

    一.什么是SPL? SPL是用于解决典型问题(standard problems)的一组接口与类的集合.(出自:http://php.net/manual/zh/intro.spl.php) SPL, ...

  3. C 标准库系列之locale.h

    locale.h 区域设置相关,主要针对时间日期.货币格式.字符控制.数字格式等以满足某区域的设置需要. locale设置类别主要包括以下几个宏定义的类别: LC_ALL:设置所有的类别: LC_CO ...

  4. C 标准库系列之errno.h

    errno.h 提供了一个整数全局变量errno,当系统调用或者库函数的错误事件发生时可能会修改该值,指明错误的原因,该值可在任何需要的地方被修改:一般情况不为0的值表示出现了异常或者错误. errn ...

  5. C 标准库系列之assert.h

    先简单介绍一下<assert.h>头文件,该头文件的目的便是提供一个宏assert的定义,即可以在程序必要的地方使用其进行断言处理:断言在程序中的作用是当在调试模式下时,若程序给出的前提条 ...

  6. C 标准库系列之概述

    基本上很多编程语言都会提供针对语言本身的一系列的标准库或者包,当然C语言同样也有提供标准库,C语言的标准库是一系列的头文件的集合:如assert.h.ctype.h.errno.h.float.h.l ...

  7. C标准库-数值字符串转换与内存分配函数

    原文链接:http://www.orlion.ga/977/ 一.数值字符串转换函数 #include <stdlib.h> int atoi(const char *nptr); dou ...

  8. Python标准库13 循环器 (itertools)

    作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! 在循环对象和函数对象中,我们了解了循环器(iterator)的功能.循环器是对象的 ...

  9. Python标准库14 数据库 (sqlite3)

    作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! Python自带一个轻量级的关系型数据库SQLite.这一数据库使用SQL语言.S ...

  10. 把《c++ primer》读薄(3-3 标准库bitset类型)

    督促读书,总结精华,提炼笔记,抛砖引玉,有不合适的地方,欢迎留言指正. //开头 #include <bitset> using std::bitset; 问题1.标准库bitset类型( ...

随机推荐

  1. nginx: [error] open() "/usr/local/nginx/nginx.pid" failed (2: No such file or directory)

    nginx 启动出现错误 nginx: [error] open() "/usr/local/nginx/nginx.pid" failed (2: No such file or ...

  2. 基于OpenSSL的密码管理系统-应用密码学课程报告

    第1章 概要设计 1.1 设计目的 本研究旨在设计并实现一个基于OpenSSL的密码管理系统,该系统具备密钥对的生成.密钥上传.密钥的核对.身份认证.文件与邮件的加密和解密.数字签名及数字证书管理等常 ...

  3. ORACLE数据挖掘之 MSET-SPRT

    虽然是熟悉的Oracle数据库,但关于机器学习.数据挖掘这方面的知识笔者起初也是不了解的,文中MSET相关设置来源于同事提供的sample,在测试过程中边查资料边学习吸收,也因此看到了别样的Oracl ...

  4. Delphi 数据库连接查询分析器

    为了方便自己查询数据库信息,花了小时间写了小工具,添加SQL语法高亮显示功能

  5. 窗体添加按钮--java进阶day03

    1.组件.面板对象 窗体中的图片.按钮.文本都是组件,光创建出了窗体没有组件肯定不行,但是这些组件该放到窗体的哪个位置? 很明显是窗体中空白的位置,但是我们需要知道,这块空白位置在窗体中是一个被封装的 ...

  6. 『Plotly实战指南』--饼图绘制高级篇

    在数据可视化的世界里,饼图是最直观的展示比例关系的工具之一. 然而,传统的静态饼图已经无法满足现代数据分析的需求.Plotly作为一款强大的可视化库,不仅提供了饼图丰富的基础功能,还支持交互效果和动态 ...

  7. study PostgreSQL【2-FireDAC连接PostgreSQL】

    就这么个简单问题,一下午时间.想想就憋屈. 那么牛逼哄哄FireDAC居然连接PostgreSQL出问题了.帮助中说的啥意思,咱也不明白.网上一通也是云里雾里. 上干货,具体点: TFDConnect ...

  8. 成都信息工程大学天梯赛 L2-2 不要刁难我们了

    赛时板子没写对,原因就是标记数组的位置放错了,放到了while外面,导致这样距离数组是不会更新的,板子还是要自己多写少看 #include <bits/stdc++.h> #define ...

  9. nginx配置代理指向Redis

    stream { upstream redis { server 127.0.0.1:6379 max_fails=3 fail_timeout=30s; #*redis-addres*替换为真实地址 ...

  10. kettle介绍-Step之Script Values/Mod(JavaScript 代码) 一

    Script Values/Mod JavaScript 代码介绍 JavaScript 代码步骤提供了一个用户界面,用户可以编写 JavaScript 代码到脚本区,脚本区域中的每一行代码都会执行一 ...