Go 并发编程实战课
晁岳攀(鸟窝)
前微博技术专家,知名微服务框架 rpcx 作者
25635 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 22 讲
Go 并发编程实战课
15
15
1.0x
00:00/00:00
登录|注册

07 | Cond:条件变量的实现机制及避坑指南

利用Cond实现一个容量有限的queue
为什么需要再检查等待条件?
容易出错的地方
使用场景
Kubernetes项目中的使用
只调用了一次Wait,没有检查等待条件是否满足
调用Wait的时候没有加锁
Wait方法
Signal和Broadcast
runtime_notifyListXXX
Wait方法
Signal方法
Broadcast方法
初始化
思考题
总结
知名项目中Cond的使用
常见错误
实现原理
基本用法
Cond
Cond的实现机制及避坑指南

该思维导图由 AI 生成,仅供参考

你好,我是鸟窝。
在写 Go 程序之前,我曾经写了 10 多年的 Java 程序,也面试过不少 Java 程序员。在 Java 面试中,经常被问到的一个知识点就是等待 / 通知(wait/notify)机制。面试官经常会这样考察候选人:请实现一个限定容量的队列(queue),当队列满或者空的时候,利用等待 / 通知机制实现阻塞或者唤醒。
在 Go 中,也可以实现一个类似的限定容量的队列,而且实现起来也比较简单,只要用条件变量(Cond)并发原语就可以。Cond 并发原语相对来说不是那么常用,但是在特定的场景使用会事半功倍,比如你需要在唤醒一个或者所有的等待者做一些检查操作的时候。
那么今天这一讲,我们就学习下 Cond 这个并发原语。

Go 标准库的 Cond

Go 标准库提供 Cond 原语的目的是,为等待 / 通知场景下的并发问题提供支持。Cond 通常应用于等待某个条件的一组 goroutine,等条件变为 true 的时候,其中一个 goroutine 或者所有的 goroutine 都会被唤醒执行。
顾名思义,Cond 是和某个条件相关,这个条件需要一组 goroutine 协作共同完成,在条件还没有满足的时候,所有等待这个条件的 goroutine 都会被阻塞住,只有这一组 goroutine 通过协作达到了这个条件,等待的 goroutine 才可能继续进行下去。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

Go标准库提供了Cond原语,用于支持等待/通知场景下的并发问题。Cond通常应用于等待某个条件的一组goroutine,等条件变为true时,其中一个或所有的goroutine都会被唤醒执行。文章介绍了Cond的基本用法,包括初始化、Broadcast、Signal和Wait方法的使用。此外,还探讨了Cond的实现原理,指出其实现逻辑相对简单,关键逻辑已被Locker或runtime的等待队列实现。文章还提到了使用Cond常见的两个错误,即调用Wait时未加锁和未检查条件是否满足就继续执行。文章深入浅出地介绍了Cond的使用方法和实现原理,对于想要了解并发编程的读者具有一定的参考价值。文章还提到了Cond在实际项目中的使用场景,以及与其他并发原语的比较,展示了Cond的独特特性和适用场景。文章最后提出了思考题,引发读者思考和讨论。整体而言,本文全面介绍了Cond的使用方法、实现原理和适用场景,对于并发编程感兴趣的读者具有一定的参考价值。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《Go 并发编程实战课》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(31)

  • 最新
  • 精选
  • 那时刻
    老师,上节课你提到noCopy,是一个辅助的、用来帮助 vet 检查用的类型,而Cond还有个copyChecker 是一个辅助结构,可以在运行时检查 Cond 是否被复制使用。 nocpoy是静态检查,copyChecker是运行时检查,不是理解是否正确? 另外不是是否有其他区别呢?

    作者回复: 是的

    2020-10-26
    13
  • chongsheng
    关于Cond还有一种会不小心误用的场景,因为一些原因导致Wait执行的时候,Signal/Broadcast就已经执行完了,导致Wait一直等待无法唤醒。比如这里的例子 https://stackoverflow.com/questions/36857167/how-to-correctly-use-sync-cond

    作者回复: 👍🏻

    2022-01-06
    2
  • myrfy
    还是没有想明白k8s为什么不能用channel通知 close可以实现broadcast的功效,在pop的时候,也是只有一个goroutine可以拿到数据,感觉除了关闭队列之外,不存在需要broadcast的情况。也就是感觉不需要多次broadcast,这样channel应该是满足要求的……请老师明示

    作者回复: 因为需要重用,使用chan close后没法重用了

    2020-10-28
    2
  • moooofly
    请教一个问题,nocpoy 是用于 vet 静态检查,copyChecker 是为了运行时检查,都是为了检查 copy 问题,为啥 Cond 要在两处检查,而 Mutex 只需要一处?

    作者回复: 你第一句给出了答案。分别应用不同阶段

    2020-10-27
    2
    1
  • kyo
    func main() { c := sync.NewCond(&sync.Mutex{}) var ready int for i := 0; i < 10; i++ { go func(i int) { time.Sleep(time.Duration(rand.Int63n(10)) * time.Second) // 加锁更改等待条件 c.L.Lock() ready++ c.L.Unlock() log.Printf("运动员#%d 已准备就绪\n", i) // 广播唤醒所有的等待者 c.Broadcast() }(i) } c.L.Lock() for ready != 10 { c.Wait() log.Println("裁判员被唤醒一次") } c.L.Unlock() //所有的运动员是否就绪 log.Println("所有运动员都准备就绪。比赛开始,3,2,1, ......") } 这个例子有问题吧. 这里的 ready 变量共享了变量 c 的锁. 会导致在 c.Wait() 语句执行前 for 中的 goroutine 全部堵塞. 在 c.Wait() 前加句 time.Sleep(time.Second * 10) 试试就知道了. 是不是应该给 ready 变量单独定义一个 Mutex?

    作者回复: wait会释放锁

    2022-12-04归属地:北京
  • Geek_b45293
    有个问题,为什么 Mutex 不使用 copyChecker 来检测是否被复制呢?

    作者回复: 因为mutex,rwmutex实现locker接口,vet工具能识别出来,waitgroup,cond没有实现locker接口,所以需要嵌入nocopy提示linter工具

    2022-09-26归属地:北京
  • 授人以🐟,不如授人以渔
    文章中在描述 Cond 的复杂性时,说明了 3 点,第三点:「条件变量的更改」 是否可需改为:「等待条件的更改」?

    作者回复: 对

    2021-11-03
  • bearlu
    老师请教一个问题,如果Wait前加锁,然后执行完Wait又Unlock有什么作用,我把Wait后面的Unlock去掉,好似程序也能正常运行。是我漏了什么?

    作者回复: 那你locker就永远没释放呗

    2021-07-24
  • 网管
    老师,Kubernetes PriorityQueue的那个Pop方法里没有使用p.cond.L.Lock()方法,他们是怎么防止不出现释放未加锁的panic啊。

    作者回复: 有锁,看方法第一二行

    2020-11-01
  • S.S Mr Lin
    每次调用wait前都要加锁,为啥加锁语句放在了fo的外面?第二次wait是不是就没有加锁了?

    作者回复: 放在外面的原因是可以利用锁保护共享数据的读写。wait总是需要锁

    2020-10-28
    2
收起评论
显示
设置
留言
31
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部