c++ 条件变量 wait(c++ 条件变量使用)

c++ 条件变量 wait(c++ 条件变量使用)条件变量中有以下两类函数 唤醒函数 直接参考链接 notify one 只唤醒队列中的第一个线程 notify all 所有线程被一个一个唤醒 先抢到锁的先唤醒 等待函数 wait std unique lock lt std mutex gt amp lock 阻塞直到被唤醒 wait std unique lock lt std

大家好,我是讯享网,很高兴认识大家。



条件变量中有以下两类函数:

唤醒函数(直接参考链接):

  • notify_one:只唤醒队列中的第一个线程
  • notify_all:所有线程被一个一个唤醒,先抢到锁的先唤醒

等待函数:

  • wait( std::unique_lock<std::mutex>& lock ):阻塞直到被唤醒
  • wait( std::unique_lock<std::mutex>& lock, Predicate pred ):阻塞,但是被唤醒时,如果函数对象pred返回为false,则继续阻塞

生产者消费者模式的代码(以下代码参考链接):

 

讯享网

互斥锁mtx的作用(避免唤醒操作被错过):还未阻塞就收到唤醒操作,从而导致唤醒操作被错过。如果没有互斥锁将会出现以下情况:
wait线程判断完不满足,但在阻塞之前notify线程修改了que并执行了notify,wait线程随后才被阻塞,这样wait线程就错过了这次唤醒,这也就是所谓的Lost wakeup问题。

的作用(防止虚假唤醒):不能替换为,这是为了防止虚假唤醒。如果替换为可能会出现如下结果:

  • th2[0]拿完了队列里最后一个产品正在处理,此时队列为空。
  • th2[1]想去队列里拿发现已经空了,所以停在了wait上。
  • th1[0]拿到mtx后,往队列添加了一个产品,并执行了notify_one通知处于等待状态的消费者。
  • 由于收到了notify,th2[1]准备要被调度,但是th2[0]此时恰好处理完了手头的任务,并进行了下一轮循环,抢在th2[1]之前拿到了mtx并取走了th1[0]刚放进去的产品,此时th2[1]被阻塞,随后th2[0]释放了mtx。
  • th2[0]释放了mtx后,th2[1]终于拿到了mtx却发现队列又是空的,这就是一次虚假唤醒,对于这种情况th2[1]需要继续wait。要想实现“继续wait”,就需要使用,而不是

虚假唤醒:被唤醒了,但是资源却被其他线程先抢走了


讯享网

wait()的第二个形参可用于代替:

讯享网

整个同步(参考自:链接):

实际上,condition_variable变量内部也有一个mutex,用于保护等待列表的修改,假设我们定义的mutex为m_a,cv内部的mutex为m_b,那么所以整个同步过程其实是

wait线程:

获取m_a,判断condition,发现不满足。
调用wait:
(1)获取m_b,获取后notify线程的notify也会被阻塞。
(2)释放m_a。
(3)线程挂到等待列表里。 释放m_b后进行等待。
(4)被唤醒后重新获取m_a进行后续操作。

notify线程:获取m_a,修改condition。
调用notify :
(1)获取m_b 。
(2)释放m_b后,通知等待列表内的线程唤醒。
释放m_a。//这一步可以在notify之前做,也就是notify不需要hold 外部mutex

核心就是wait线程会带着外部的锁来获取等待队列锁,这把队列锁用于:wait线程push线程到等待队列和notify线程进行notify操作。而notify线程修改condition之前也需要获得外部锁,因此只要wait线程先拿到外部锁可以确保也会先拿到等待队列锁,确保了wait线程在确定需要wait但真正push到等待列表之前的这段时间notify线程没法进行notify,避免错过唤醒。

参考自:链接。

条件变量是非常底层的同步原语,很少直接使用,一般都是用它来实现高层的同步措施,如或。

倒计时(CountDownLatch)是一种常用且易用的同步手段。它主要有两种用途:

  • 主线程发起多个子线程,等这些子线程各自都完成一定的任务之后,主线程才继续执行。通常用于主线程等待多个子线程完成初化。
  • 主线程发起多个子线程,子线程都等待主线程,主线程完成其他一些任务之后通知所有子线程开始执行。通常用于多个子线程等待主线程发出“起跑”命令

的实现如下(参考:链接):

 

参考了muduo的BlockingQueue的实现,用C++11改写(参考链接):

讯享网

 

put函数每次添加元素都会调用notify_one(),如果更改为只在队列大小由0到1是调用notify_one(),由于生产者线程一直在生产产品,那么极端情况下队列可能一直不为空,则不会唤醒其他消费者线程消费产品,而是一直使用同一个消费者线程消费产品。这就会导致有一个消费者线程一直在消费产品,而其他的消费者线程永远无法消费产品。

小讯
上一篇 2025-06-08 15:57
下一篇 2025-05-08 09:03

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/161413.html