condition_variable介绍

在C++11中,我们可以使用条件变量(condition_variable)实现多个线程间的同步操作;当条件不满足时,相关线程被一直阻塞,直到某种条件出现,这些线程才会被唤醒。

其主要成员函数如下:

条件变量是利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:

  1. 一个线程因等待"条件变量的条件成立"而挂起;
  2. 另外一个线程使"条件成立",给出信号,从而唤醒被等待的线程。

为了防止竞争,条件变量的使用总是和一个互斥锁结合在一起;通常情况下这个锁是std::mutex,并且管理这个锁 只能是 std::unique_lock<std::mutex> RAII模板类。

上面提到的两个步骤,分别是使用以下两个方法实现:

  • 等待条件成立使用的是condition_variable类成员wait 、wait_for 或 wait_until。
  • 给出信号使用的是condition_variable类成员notify_one或者notify_all函数。

细节说明

在条件变量中只能使用std::unique_lock<std::mutex>说明

unique_lock和lock_guard都是管理锁的辅助类工具,都是RAII风格;它们是在定义时获得锁,在析构时释放锁。它们的主要区别在于unique_lock锁机制更加灵活,可以再需要的时候进行lock或者unlock调用,不非得是析构或者构造时。它们的区别可以通过成员函数就可以一目了然。

wait/wait_for说明

线程的阻塞是通过成员函数wait()/wait_for()/wait_until()函数实现的。这里主要说明前面两个函数:

  • wait()成员函数

    函数声明如下:

    void wait( std::unique_lock<std::mutex>& lock );
    //Predicate 谓词函数,可以普通函数或者lambda表达式
    template< class Predicate >
    void wait( std::unique_lock<std::mutex>& lock, Predicate pred );
    

    wait 导致当前线程阻塞直至条件变量被通知,或虚假唤醒发生,可选地循环直至满足某谓词

  • wait_for()成员函数

    函数声明如下:

    (1)
    template< class Rep, class Period >
    std::cv_status wait_for( std::unique_lock<std::mutex>& lock,const std::chrono::duration<Rep, Period>& rel_time);(2)
    template< class Rep, class Period, class Predicate >
    bool wait_for( std::unique_lock<std::mutex>& lock,const std::chrono::duration<Rep, Period>& rel_time,Predicate pred);
    

    wait_for 导致当前线程阻塞直至条件变量被通知,或虚假唤醒发生,或者超时返回

    返回值说明:

    (1)若经过 rel_time 所指定的关联时限则为 std::cv_status::timeout ,否则为 std::cv_status::no_timeout 。

    (2)若经过 rel_time 时限后谓词 pred 仍求值为 false 则为 false ,否则为 true 。

以上两个类型的wait函数都在会阻塞时,自动释放锁权限,即调用unique_lock的成员函数unlock(),以便其他线程能有机会获得锁。这就是条件变量只能和unique_lock一起使用的原因,否则当前线程一直占有锁,线程被阻塞。

notify_all/notify_one

notify函数声明如下:

void notify_one() noexcept;
若任何线程在 *this 上等待,则调用 notify_one 会解阻塞(唤醒)等待线程之一。void notify_all() noexcept;
若任何线程在 *this 上等待,则解阻塞(唤醒)全部等待线程。

虚假唤醒

在正常情况下,wait类型函数返回时要不是因为被唤醒,要不是因为超时才返回,但是在实际中发现,因此操作系统的原因,wait类型在不满足条件时,它也会返回,这就导致了虚假唤醒。因此,我们一般都是使用带有谓词参数的wait函数,因为这种(xxx, Predicate pred )类型的函数等价于:

while (!pred()) //while循环,解决了虚假唤醒的问题
{wait(lock);
}

原因说明如下:

假设系统不存在虚假唤醒的时,代码形式如下:

if (不满足xxx条件)
{//没有虚假唤醒,wait函数可以一直等待,直到被唤醒或者超时,没有问题。//但实际中却存在虚假唤醒,导致假设不成立,wait不会继续等待,跳出if语句,//提前执行其他代码,流程异常wait();
}//其他代码
...

正确的使用方式,使用while语句解决:

while (!(xxx条件) )
{//虚假唤醒发生,由于while循环,再次检查条件是否满足,//否则继续等待,解决虚假唤醒wait();
}
//其他代码
....

条件变量使用

在这里,我们使用条件变量,解决生产者-消费者问题,该问题主要描述如下:

生产者-消费者问题,也称有限缓冲问题,是一个多进程/线程同步问题的经典案例。该问题描述了共享固定大小缓冲区的两个进程/线程——即所谓的“生产者”和“消费者”,在实际运行时会发生的问题。

生产者的主要作用是生成一定量的数据放到缓冲区中,然后重复此过程。与此同时,费者也在缓冲区消耗这些数据。该问题的关键就是要保证生产者不会在缓冲区满时加入数据,消费者也不会在缓冲区中空时消耗数据。

要解决该问题,就必须让生产者在缓冲区满时休眠(要么干脆就放弃数据),等到下次消费者消耗缓冲区中的数据的时候,生产者才能被唤醒,开始往缓冲区添加数据

同样,也可以让消费者在缓冲区空时进入休眠,等到生产者往缓冲区添加数据之后,再唤醒消费者

生产者-消费者代码如下:

std::mutex g_cvMutex;
std::condition_variable g_cv;//缓存区
std::deque<int> g_data_deque;
//缓存区最大数目
const int  MAX_NUM = 30;
//数据
int g_next_index = 0;//生产者,消费者线程个数
const int PRODUCER_THREAD_NUM  = 3;
const int CONSUMER_THREAD_NUM = 3;void  producer_thread(int thread_id)
{while (true){std::this_thread::sleep_for(std::chrono::milliseconds(500));//加锁std::unique_lock <std::mutex> lk(g_cvMutex);//当队列未满时,继续添加数据g_cv.wait(lk, [](){ return g_data_deque.size() <= MAX_NUM; });g_next_index++;g_data_deque.push_back(g_next_index);std::cout << "producer_thread: " << thread_id << " producer data: " << g_next_index;std::cout << " queue size: " << g_data_deque.size() << std::endl;//唤醒其他线程 g_cv.notify_all();//自动释放锁}
}void  consumer_thread(int thread_id)
{while (true){std::this_thread::sleep_for(std::chrono::milliseconds(550));//加锁std::unique_lock <std::mutex> lk(g_cvMutex);//检测条件是否达成g_cv.wait( lk,   []{ return !g_data_deque.empty(); });//互斥操作,消息数据int data = g_data_deque.front();g_data_deque.pop_front();std::cout << "\tconsumer_thread: " << thread_id << " consumer data: ";std::cout << data << " deque size: " << g_data_deque.size() << std::endl;//唤醒其他线程g_cv.notify_all();//自动释放锁}
}int main()
{std::thread arrRroducerThread[PRODUCER_THREAD_NUM];std::thread arrConsumerThread[CONSUMER_THREAD_NUM];for (int i = 0; i < PRODUCER_THREAD_NUM; i++){arrRroducerThread[i] = std::thread(producer_thread, i);}for (int i = 0; i < CONSUMER_THREAD_NUM; i++){arrConsumerThread[i] = std::thread(consumer_thread, i);}for (int i = 0; i < PRODUCER_THREAD_NUM; i++){arrRroducerThread[i].join();}for (int i = 0; i < CONSUMER_THREAD_NUM; i++){arrConsumerThread[i].join();}return 0;
}

运行结果:

C++11条件变量使用详解相关推荐

  1. C++条件变量使用详解

    1. condition_variable介绍 在C++11中,我们可以使用条件变量(condition_variable)实现多个线程间的同步操作:当条件不满足时,相关线程被一直阻塞,直到某种条件出 ...

  2. C 条件变量使用详解

    condition_variable介绍 在C 11中,我们可以使用条件变量(condition_variable)实现多个线程间的同步操作:当条件不满足时,相关线程被一直阻塞,直到某种条件出现,这些 ...

  3. 相关性质和条件变量-ReentrantLock详解(2)-AQS-并发编程(Java)

    文章目录 1 可重入 2 可打断 3 公平锁 4 条件变量 4.1 await() 4.1.1 主方法 4.1.2 addConditionWaiter() 4.1.3 isOnSyncQueue() ...

  4. ThinkPHP模版引擎之变量输出详解

    ThinkPHP模版引擎之变量输出详解 使用ThinkPHP开发有一定时间了,今日对ThinkPHP的模板引擎变量解析深入了解了一下,做出一些总结,分享给大家供大家参考.具体分析如下: 我们已经知道了 ...

  5. python变量类型-Python 变量类型详解

    变量存储在内存中的值.这就意味着在创建变量时会在内存中开辟一个空间. 基于变量的数据类型,解释器会分配指定内存,并决定什么数据可以被存储在内存中. 因此,变量可以指定不同的数据类型,这些变量可以存储整 ...

  6. Nginx内置变量以及日志格式变量参数详解

    Nginx内置变量以及日志格式变量参数详解 $args #请求中的参数值 $query_string #同 $args $arg_NAME #GET请求中NAME的值 $is_args #如果请求中有 ...

  7. on 和where条件的放置详解

    on 和where条件的放置详解 on 和where条件的放置详解 总结: 对于left join,不管on后面跟什么条件,左表的数据全部查出来,因此要想过滤需把条件放到where后面 对于inner ...

  8. python的shutil模块是内置的_Python之shutil模块11个常用函数详解,python内置函数是什么...

    Python之shutil模块11个常用函数详解,python内置函数是什么 shutil 是 Python 中的高级文件操作模块,与os模块形成互补的关系,os主要提供了文件或文件夹的新建.删除.查 ...

  9. kubernetes系列11—PV和PVC详解

    kubernetes系列11-PV和PVC详解 原文:kubernetes系列11-PV和PVC详解 本文收录在容器技术学习系列文章总目录 1.认识PV/PVC/StorageClass 1.1 介绍 ...

最新文章

  1. Electron中通过process进程对象的api获取CPU、系统位数、环境变量等相关信息
  2. Follow My Logic 1048 PKU
  3. FZU - 2042 The Mad Mathematician 数位dp + 算贡献
  4. Linux笔记-设置SSH公钥免密码登录
  5. 冗余机器人以及雅克比伪逆矩阵
  6. JavaScript中call,apply,bind方法的总结
  7. P1403 [AHOI2005]约数研究
  8. js 获取 sharepoint 2010 网站信息(title,id,language……)
  9. Win socket编程--IOCP完成端口模型
  10. pdf文档怎样转换成word文档?2022pdf转word软件推荐
  11. 白盒测试-条件组合覆盖
  12. 网关的作用(两个内网主机通信原理)
  13. 企业终端病毒查杀+漏洞及补丁管理(自写知识分享,不是广告)
  14. 幽游白书雕像:岁月流转,闪耀如初,我回来了
  15. cf 487E/uoj 30 Tourists
  16. mysql workbench pk_MySQL Workbench筑表时PK, NN, UQ, BIN, UN, ZF, AL的意思
  17. 首席数据官:从哪里来?到哪里去?
  18. LuatOS快速上手——GPS定位
  19. class torch.optim.lr_scheduler.ExponentialLR
  20. 收藏夹功能打开解析视频连接

热门文章

  1. 关于getMethod()方法
  2. Unity之几种Json库比对
  3. 2台mysql高可用性_MySQL 高可用性keepalived+mysql双主
  4. 首次开放测试TCL QUHD让外界近距离接触量子点技术
  5. 12306车站信息爬取(3)——余票信息的查询(内含prettytable和colorama模块)
  6. python爬虫爬取网页指定数据(百度百科)
  7. 推荐 4 个 火火火火 的开源项目
  8. Ubuntu快速更换国内源
  9. 软考高项-信息系统项目管理师-必考知识点5
  10. vim的python代码检测工具