28 | 条件变量sync.Cond (下)
该思维导图由 AI 生成,仅供参考
问题 1:条件变量的Wait方法做了什么?
- 深入了解
- 翻译
- 解释
- 总结
条件变量sync.Cond是Go语言中基于互斥锁的一种同步工具。文章首先解答了条件变量的Wait方法的内部机制,强调了在调用Wait方法前必须先锁定条件变量基于的互斥锁,并使用for语句包裹Wait方法的调用以重复检查共享资源的状态。其次,文章对比了条件变量的Signal方法和Broadcast方法的异同,指出Signal方法只唤醒一个等待通知的goroutine,而Broadcast方法则唤醒所有等待通知的goroutine。此外,还强调了这两个方法不需要受到互斥锁的保护,并提醒条件变量的通知具有即时性。最后,文章提出了一个思考题,探讨了sync.Cond类型中的公开字段L的作用以及是否可以在使用条件变量的过程中改变这个字段的值。整体而言,文章内容涵盖了条件变量的基本使用方法和注意事项,适合读者快速了解条件变量的概述。
《Go 语言核心 36 讲》,新⼈⾸单¥59
全部留言(45)
- 最新
- 精选
- 不记年老师,我有一个疑问,对于cond来说,每次只唤醒一个goruntine,如果这么goruntine发现消息不是自己想要的就会从新阻塞在wait函数中,那么真正需要这个消息的goruntine还会被唤醒吗?
作者回复: 不会,所以我才说应该优先用 broadcast。
2019-07-2420 - LaughingL公开变量代表cond初始化时传递进来的锁,这个锁的状态是可以改变的,但会影响cond对互斥锁的控制。
作者回复: 动这个L之前一定要三思,谨慎些,想想是不是会影响到程序。
2018-10-1515 - Lywane``` lock.Lock() for mailbox == 1 { sendCond.Wait() } mailbox = 1 lock.Unlock() recvCond.Signal() ``` 只要共享资源的状态不变,即使当前的 goroutine 因收到通知而被唤醒,也依然会再次执行这个Wait方法,并再次被阻塞。 老师我对这句话有个疑问,假设Wait不解锁,直接阻塞了当前goroutine,那么当收到通知时,mailbox的值应该已经被改成0了,此时唤醒,应该不满足for循环条件了呀,为什么会再次执行Wait方法呢? 我思考的结论是:Wait解锁是为了让其他goroutine去修改mailbox的值,不知道这么理解对吗。
作者回复: 首先还是要明确这个过程:sendCond.Wait() 会先解锁再阻塞当前的 goroutine,然后等到别的地方调用 sendCond.Signal() 后,这里的 sendCond.Wait() 会加锁并唤醒当前的 goroutine。 假设有多个 goroutine 都调用了 sendCond.Wait() 方法,如果它们所在的 goroutine 都因条件变量的通知而被唤醒,那么(由于锁的缘故)只有一个 goroutine (以下称“1号G”)能够率先再次检查 mailbox。 “1号G”发现 mailbox 的值已经不是 1 了,随即退出循环,并且又把 mailbox 的值设置成了 1。最后解锁,并给 recvCond 发通知。 由于“1号G”的解锁,“2号G”、“3号G”等等(其他调用了 sendCond.Wait() 方法的G)都会竞争这个锁。得到该锁控制权的某个G会再锁住这个锁,然后发现 mailbox 的值依然是 1,随即再次调用 sendCond.Wait() 方法,并继续等待这个条件变量的下一个通知。其他的G也是类似的。 这个场景和过程要想清楚。可以自己画几条平行线,然后模拟一下这个过程。
2020-03-275 - Geek_a8be59老实您好,不太明白为什么一定要用条件变量呢。我看了 go并发编程第2版和你这边的做了对应,书上是已生成者消费者为例,我想的是用两个互斥量不行么?生成一个锁,消费一个锁,只是说可能会浪费在循环判断是否可生成,是否可消费中,还有可能因为某些不成功导致一直不能解锁的状况。 难不成条件变量主要就是优化上述问题的么?
作者回复: 我记得在书里也说过类似的内容:条件变量常用于对共享资源的状态变化的监控和响应(由此可以实现状态机),同时也比较适合帮助多个线程协作操作同一个共享资源(这里的关键词是协作)。 条件变量有两种通知机制,这是互斥量没有的。互斥量只能保护共享资源(这里的关键词是保护),功能比较单一。所以条件变量在一些场景下会更高效。 你自己也说出了一些使用两个互斥锁来做的弊端。这些弊端其实就已经比较闹心了。一个是用两个锁对性能的影响,一个是两个锁如果配合不当就会造成局部的死锁。这还是多方协作操作同一个共享资源中最简单的情况。 再加上我前面说的那些,条件变量在此类场景下的优势已经非常明显了。注意它在Wait时还有原子级的操作呢。
2019-08-122 - 樂文💤所以如果用的signal方法只通知一个go routine的话 条件变量的判断方法改成if应该是没问题的 但如果是多个go routine 同时被唤醒就可能导致多个go routine 在不满足唤醒状态的时候被唤醒而导致处理错误,这时候就必须用for来不断地进行检测可以这么理解么
作者回复: 总是需要用 for 的,因为你没法保证收到信号之后那个状态就一定是你想要的。
2019-08-0332 - 手指饼干老师您好,关于发送通知是否需要在锁的保护下调用,还是有些疑问,以下想法请老师帮忙看看是否理解正确: 一个在等待通知的 goroutine 收到通知信号被唤醒,接下来执行的是条件变量 c.L.Lock() 操作,无论信号的发送方是否是在锁的保护下发送信号,该 goroutine 已经不是在等待通知的状态了,而是在尝试获取锁的状态,即使被阻塞,也是因为获取不到锁。区别只是,如果信号发送方在 Unlock() 之后发送信号,那么该 goroutine 被唤醒后获得锁可能会衔接得更好一点。 对于某些场景,比如说函数的开头两行代码就是 mutex.Lock() defer mutex.Unlock() 这种情况是否可以允许通知在 Unlock() 之前调用
作者回复: 首先要明确: 条件变量的这两个方法并不需要受到互斥锁的保护,我们也最好不要在解锁互斥锁之前调用它们。 其次: 等待方法对锁的操作主要是为了让多个等待方有秩序的行事。在真正等待前释放锁,是为了让其他的等待方也具有进入等待状态的条件。在唤醒之后再次试图持有锁,是为了让多个等待方串行地检查条件是否满足以及执行后续的操作。 所以,结论是: 发送方在发送通知的时候最好不要持有等待方操作的那个锁。而等待方,应该在等待的时候持有锁加以保护。 最后: 关于你的最后一个问题,如果 mutex 是等待方操作的那个锁,那么就有可能产生发送方与等待方的互斥问题。由于等待方会在真正等待之前释放锁,所以应该不会产生完全死锁的情况。但是,这种没必要的操作肯定会影响程序的效率。 如果 mutex 与等待方没关系,那就无所谓了。你可以根据“是否想串行化通知的发送操作”来决定是否加锁。不过这通常也没什么必要。
2020-08-011 - 黄仲辉sync.crod 类比 java的 监视器锁
作者回复: sync.Cond其实对应的是操作系统API中的条件变量。我记得Java也有条件变量这个东西吧。
2022-09-04归属地:北京 - Geek_108cb5试了一下把互斥锁改成读写锁, 中途会发生一个发送消息被两个接受者同时收到的场景, 导致最后发送者阻塞, 此时主线程的信道也阻塞了, 接受者协程执行完毕, 整体是死锁的情况。 那假如发送者和接收者都是无限循环, 而且多个接收者接收者接到同一份消息不会对业务有影响的情况下, 使用读写锁应该也是没有问题的?
作者回复: 如果你用的是读写锁的话,读写锁中的读锁定操作之间是不互斥的。所以我估计是你这块的用法错了。
2022-03-01 - HiNeNifunc testCond() { mu := sync.Mutex{} cond := sync.NewCond(&mu) s := "" go func () { fmt.Println("This is consumer") for { mu.Lock() for len(s) == 0 { cond.Wait() } //time.Sleep(time.Second * 1) fmt.Println("consumer, s:", s) s = "" mu.Unlock() cond.Signal() } }() go func () { fmt.Println("This is producer") for { mu.Lock() for len(s) > 0 { cond.Wait() } fmt.Println("produce a string") s = "generate s resource" mu.Unlock() cond.Signal() } }() time.Sleep(time.Second * 10) } ========================================== 自己测的时候发现用一个Cond就可以满足生产者消费者的简单模型,谁能告诉我为什么例子一定要使用两个Cond?
作者回复: 因为核心需求不同。在你的例子只需要纯粹单向的生产-消费(这就是最简单的情况)。但在专栏的例子里,双方属于“秘密接触”,需要尽可能少的“露面”和“访问邮箱”。所以才需要互相通知。 需求决定代码。
2021-11-11 - 传说中的成大大看了你下面的留言 感觉条件变量主要是为了避免互斥锁或者读写锁 锁竞争条件
作者回复: 条件变量的特色是“协调”,而不是“互斥”。但是你也看到了,它是基于锁的。
2020-04-07