14 C++11线程同步之条件变量
创始人
2024-02-06 08:04:51
0

在学习条件变量之前需要先了解下std::unique_lock;条件变量 condition_variable需要配合std::unique_lock使用;

std::unique_lock

std::unique_lock的详细细节参考此篇文章。

C11条件变量

条件变量是 C++11 提供的另外一种用于等待的同步机制,它能阻塞一个或多个线程,直到收到另外一个线程发出的通知或者超时时,才会唤醒当前阻塞的线程。条件变量需要和互斥量配合起来使用,C++11 提供了两种条件变量:

  • condition_variable:需要配合std::unique_lockstd::mutex进行wait操作,也就是阻塞线程操作;
  • condition_variable_any:可以和任意带有lock()、unlock()语意的mutex搭配使用:
    • std::mutex:独占的非递归互斥锁
    • std::timed_mutex:带超时的独占非递归互斥锁
    • std::recursive_mutex:不带超时功能的递归互斥锁
    • std::recursive_timed_mutex:带超时的递归互斥锁

1. condition_variable

1.1 成员函数

condition_variable的成员函数主要分为两部分:线程阻塞函数和线程通知函数,这些函数定于头文件

// ①
void wait (unique_lock& lck);
// ②
template 
void wait (unique_lock& lck, Predicate pred);
//③
template 
cv_status wait_for (unique_lock& lck,const chrono::duration& rel_time);
//④
template 
bool wait_for(unique_lock& lck,const chrono::duration& rel_time, Predicate pred);
//⑤
template 
cv_status wait_until (unique_lock& lck,const chrono::time_point& abs_time);
//⑥
template 
bool wait_until (unique_lock& lck,const chrono::time_point& abs_time, Predicate pred);
//⑦
void notify_one() noexcept;
//⑧
void notify_all() noexcept;                 

函数①:调用该函数的线程直接被阻塞

函数②:该函数的第二个参数是一个判断条件,是一个返回值为布尔类型的函数

该参数可以传递一个有名函数的地址,也可以直接指定一个匿名函数
表达式返回false当前线程被阻塞,表达式返回true当前线程不会被阻塞,继续向下执行

函数③、④:
wait_for() 函数和 wait() 的功能是一样的,只不过多了一个阻塞时长,假设阻塞的线程没有被其他线程唤醒,当阻塞时长用完之后,线程就会自动解除阻塞,继续向下执行。
函数⑤、⑥:
wait_until() 函数和 wait_for() 的功能是一样的,它是指定让线程阻塞到某一个时间点,假设阻塞的线程没有被其他线程唤醒,当到达指定的时间点之后,线程就会自动解除阻塞,继续向下执行。

如果线程被该函数阻塞,这个线程会释放占有的互斥锁的所有权,当阻塞解除之后这个线程会重新得到互斥锁的所有权,继续向下执行(这个过程是在函数内部完成的,了解这个过程即可,其目的是为了避免线程的死锁)

函数⑦、⑧
notify_one():唤醒一个被当前条件变量阻塞的线程
notify_all():唤醒全部被当前条件变量阻塞的线程

eg:

#include 
#include 
#include 
#include 
#include 
#include using namespace std;class SyncQueue
{
public:SyncQueue(int maxSize) :m_maxSize(maxSize) {}void put(const int& x){unique_lock locker(m_mutex);//while (m_queue.size() == m_maxSize)//{//    cout << "任务队列已满,请耐心等待..." << endl;//    m_notFull.wait(locker);//}m_notFull.wait(locker, [this]() { return m_maxSize != m_queue.size();});m_queue.push_back(x);cout << x << "被生产" << endl;m_notEmpty.notify_one();}int take(){unique_lock locker(m_mutex);//while (m_queue.empty())//{//    cout << "任务队列已空,请耐心等待..." << endl;//    m_notEmpty.wait(locker);//}m_notEmpty.wait(locker, [this]() { return m_maxSize != m_queue.size(); });int x = m_queue.front();m_queue.pop_front();m_notFull.notify_one();cout << x << "被消费" << endl;return x;}private:list m_queue;mutex m_mutex;condition_variable m_notEmpty;condition_variable m_notFull;int m_maxSize;};int main()
{SyncQueue taskQ(50);auto produce = bind(&SyncQueue::put, &taskQ, placeholders::_1);auto consume = bind(&SyncQueue::take, &taskQ);thread t1[3];thread t2[3];for (int i = 0; i < 3; i++){t1[i] = thread(produce, i + 100);t2[i] = thread(consume);}for (int i = 0; i < 3; ++i){t1[i].join();t2[i].join();}system("pause");return 0;
}   

在这里插入图片描述

2. condition_variable_any

condition_variable_any 用法与condition_variable类似,区别如下:
condition_variable 配合 unique_lock 使用更灵活一些,可以在在任何时候自由地释放互斥锁,而 condition_variable_any 如果和 lock_guard 一起使用必须要等到其生命周期结束才能将互斥锁释放。但是,condition_variable_any 可以和多种互斥锁配合使用,应用场景也更广,而 condition_variable 只能和独占的非递归互斥锁(mutex)配合使用,有一定的局限性。

具体用法参考该文章

3. 案例

看下LeetCode上一个题,用条件变量来解:
给你一个类:
在这里插入图片描述
在这里插入图片描述

相关内容

热门资讯

喜欢穿一身黑的男生性格(喜欢穿... 今天百科达人给各位分享喜欢穿一身黑的男生性格的知识,其中也会对喜欢穿一身黑衣服的男人人好相处吗进行解...
发春是什么意思(思春和发春是什... 本篇文章极速百科给大家谈谈发春是什么意思,以及思春和发春是什么意思对应的知识点,希望对各位有所帮助,...
网络用语zl是什么意思(zl是... 今天给各位分享网络用语zl是什么意思的知识,其中也会对zl是啥意思是什么网络用语进行解释,如果能碰巧...
为什么酷狗音乐自己唱的歌不能下... 本篇文章极速百科小编给大家谈谈为什么酷狗音乐自己唱的歌不能下载到本地?,以及为什么酷狗下载的歌曲不是...
华为下载未安装的文件去哪找(华... 今天百科达人给各位分享华为下载未安装的文件去哪找的知识,其中也会对华为下载未安装的文件去哪找到进行解...
家里可以做假山养金鱼吗(假山能... 今天百科达人给各位分享家里可以做假山养金鱼吗的知识,其中也会对假山能放鱼缸里吗进行解释,如果能碰巧解...
四分五裂是什么生肖什么动物(四... 本篇文章极速百科小编给大家谈谈四分五裂是什么生肖什么动物,以及四分五裂打一生肖是什么对应的知识点,希...
怎么往应用助手里添加应用(应用... 今天百科达人给各位分享怎么往应用助手里添加应用的知识,其中也会对应用助手怎么添加微信进行解释,如果能...
一帆风顺二龙腾飞三阳开泰祝福语... 本篇文章极速百科给大家谈谈一帆风顺二龙腾飞三阳开泰祝福语,以及一帆风顺二龙腾飞三阳开泰祝福语结婚对应...
美团联名卡审核成功待激活(美团... 今天百科达人给各位分享美团联名卡审核成功待激活的知识,其中也会对美团联名卡审核未通过进行解释,如果能...