当前位置: 首页 > news >正文

c++中std::condition_variable最全用法归纳

前言

建议阅读以下文章前需先对建立 std::thread 多线程与std::mutex 锁有一定程度的熟悉
std::thread最全用法归纳
std::mutex最全用法归纳

概括

使用 std::condition_variable 的 wait 会把目前的线程 thread 停下来并且等候事件通知,而在另一个线程中可以使用 std::condition_variable 的 notify_one 或 notify_all 发送通知那些正在等待的事件,在多线程中经常使用,以下将介绍 std::condition_variable 具体用法,并展示一些范例

condition_variable 常用成员函数:
- wait:阻塞当前线程直到条件变量被唤醒
- notify_one:通知一个正在等待的线程
- notify_all:通知所有正在等待的线程使用 wait 必须搭配 std::unique_lock<std::mutex> 一起使用

范例1 用 notify_one 通知一个正在 wait 的线程

先梳理流程,该例先开一个新的线程 worker_thread 然后使用 wait 等待
此时 worker_thread 会阻塞(block)直到事件通知才会被唤醒
之后 main 主程序延迟个 5 ms 再使用 notify_one 发送
之后 worker_thread 收到 来自主线程的事件通知就离开 wait 继续往下 cout 完就结束该线程
主程序延迟 5ms 是避免一开始线程还没建立好来不及 wait 等待通知,主程序就先发送 notify_one 事件通知

#include <iostream>
#include <string>
#include <thread>
#include <mutex>
#include <condition_variable>std::mutex m;
std::condition_variable cond_var;void worker_thread()
{std::unique_lock<std::mutex> lock(m);std::cout << "worker_thread() wait\n";cond_var.wait(lock);// after the wait, we own the lock.std::cout << "worker_thread() is processing data\n";
}int main()
{std::thread worker(worker_thread);std::this_thread::sleep_for(std::chrono::milliseconds(5));std::cout << "main() notify_one\n";cond_var.notify_one();worker.join();std::cout << "main() end\n";
}

输出如下

worker_thread() wait
main() notify_one
worker_thread() is processing data
main() end

范例2 用 notify_all 通知全部多个 wait 等待的线程

该例主要目的是建立5个线程并等待通知
之后主程序执行go函数里的cond_var.notify_all()通知所有正在等待的线程
这5个线程分别收到通知后从wait函数离开,之后检查ready为true则离开循环
接着打印thread id然后结束该线程

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>std::mutex m;
std::condition_variable cond_var;
bool ready = false;void print_id(int id) {std::unique_lock<std::mutex> lock(m);while (!ready) {cond_var.wait(lock);}std::cout << "thread " << id << '\n';
}void go() {std::unique_lock<std::mutex> lock(m);ready = true;cond_var.notify_all();
}int main()
{std::thread threads[5];// spawn 5 threads:for (int i=0; i<5; ++i)threads[i] = std::thread(print_id,i);std::cout << "5 threads ready to race...\n";go();for (auto& th : threads)th.join();return 0;
}

输出如下,可见这5个线程不按顺序地收到通知并且各别印出thread id

5 threads ready to race...
thread 4
thread 1
thread 2
thread 3
thread 0

该例中多使用了一个额外的 ready 变量来辅助判断,也间接介绍了cond_var.wait的另一种用法
使用一个 while 循环来不断检查 ready 变量,条件不成立的话就cond_var.wait继续等待
等到下次cond_var.wait被唤醒又会再度检查这个 ready 值,一直循环检查下去
该技巧在某些情形下可以避免假唤醒这个问题
简单说就是「cond_var.wait被唤醒后还要多判断一个 bool 变量,一定要条件成立才会结束等待,否则继续等待」
注意,其中while写法

while (!ready) {cond_var.wait(lock);
}

可以简化写成如下,亦即 wait 的另一种用法,多带一个关键词在第二个参数,范例3会介绍

cond_var.wait(lock, []{return ready;});

因为 wait 内部的实现方法如下,等价于上面这种写法

template<typename _Predicate>
void wait(unique_lock<mutex>& __lock, _Predicate __p)
{while (!__p())wait(__lock);
}

范例3 wait 等待通知且有条件的结束等待

范例2简单提及cond_var.wait带入第二个参数的用法,所以本范例来实际演练这个用法
该例中,worker_thread里的cond_var.wait第一参数传入一个 unique_lock 锁
第二个参数传入一个有返回的函数,来判断是否要停止等待,回传一个 bool 变量
如果回传 true ,condition_variable 停止等待、继续往下执行
如果回传 false ,则重新开始等待下一个通知
因此等价于while (!pred()) { wait(lock); }
注意 main 里是有一个 lock_guard 与 unique_lock,worker_thread 里有一个 unique_lock

#include <iostream>
#include <string>
#include <thread>
#include <mutex>
#include <condition_variable>std::mutex m;
std::condition_variable cond_var;
std::string data;
bool ready = false;
bool processed = false;void worker_thread()
{// Wait until main() sends datastd::unique_lock<std::mutex> lock(m);std::cout << "worker_thread() wait\n";cond_var.wait(lock, []{return ready;});// after the wait, we own the lock.std::cout << "worker_thread() is processing data\n";data += " after processing";// Send data back to main()processed = true;std::cout << "worker_thread() signals data processing completed\n";// Manual unlocking is done before notifying, to avoid waking up// the waiting thread only to block again (see notify_one for details)lock.unlock();cond_var.notify_one();
}int main()
{std::thread worker(worker_thread);data = "Example data";// send data to the worker thread{std::lock_guard<std::mutex> lock(m);ready = true;std::cout << "main() signals data ready for processing\n";}cond_var.notify_one();// wait for the worker{std::unique_lock<std::mutex> lock(m);cond_var.wait(lock, []{return processed;});}std::cout << "Back in main(), data = " << data << '\n';worker.join();
}

输出如下

main() signals data ready for processing
worker_thread() wait
worker_thread() is processing data
worker_thread() signals data processing completed
Back in main(), data = Example data after processing

范例4 典型的生产者与消费者案例

在设计模式(design pattern)中,这是一个典型的生产者与消费者(producer-consumer)的例子
范例里有一位生产者每1秒生产了1个东西放到 condvarQueue 里,
这个 condvarQueue 会在去通知消费者,消费者收到通知后从 queue 里拿出这个东西来做事情,看明白该例会有很大帮助

#include <iostream>
#include <thread>
#include <queue>
#include <chrono>
#include <mutex>
#include <condition_variable>class condvarQueue
{std::queue<int> produced_nums;std::mutex m;std::condition_variable cond_var;bool done = false;bool notified = false;
public:void push(int i){std::unique_lock<std::mutex> lock(m);produced_nums.push(i);notified = true;cond_var.notify_one();}template<typename Consumer>void consume(Consumer consumer){std::unique_lock<std::mutex> lock(m);while (!done) {while (!notified) {  // loop to avoid spurious wakeupscond_var.wait(lock);}while (!produced_nums.empty()) {consumer(produced_nums.front());produced_nums.pop();}notified = false;}}void close(){{std::lock_guard<std::mutex> lock(m);done = true;notified = true;}cond_var.notify_one();}
};int main()
{condvarQueue queue;std::thread producer([&]() {for (int i = 0; i < 5; ++i) {std::this_thread::sleep_for(std::chrono::seconds(1));std::cout << "producing " << i << '\n';queue.push(i);}queue.close();});std::thread consumer([&]() {queue.consume([](int input){std::cout << "consuming " << input << '\n';});});producer.join();consumer.join();
}

输出如下

producing 0
consuming 0
producing 1
consuming 1
producing 2
consuming 2
producing 3
consuming 3
producing 4
consuming 4

归纳

等待的线程应有下列几个步骤:

  1. 获得 std::unique_lock 锁,并用该锁来保护共享变量
  2. 检查有没有满足结束等待的条件,以预防数据早已经被更新与被通知了
  3. 执行 wait 等待,wait 操作会自动释放该 mutex 并且暂停该线程
  4. 当 condition variable 通知时,该线程被唤醒,且该mutex自动被重新获得,该线程应该检查一些条件决定要不要继续等待

通知的线程应有下列几个步骤:

  1. 获取一个 std::mutex (通常通过std::lock_guard)
  2. 在上锁的范围内完成变量的修改
  3. 执行 std::condition_variable 的notify_one/notify_all

相关文章:

c++中std::condition_variable最全用法归纳

前言 建议阅读以下文章前需先对建立 std::thread 多线程与std::mutex 锁有一定程度的熟悉 std::thread最全用法归纳 std::mutex最全用法归纳 概括 使用 std::condition_variable 的 wait 会把目前的线程 thread 停下来并且等候事件通知&#xff0c;而在另一个线程中可以使用…...

Python数据可视化:数据关系图表可视化

目录 1、散点图 1.1、趋势显示的二维散点图 1.2、分布显示的二维散点图 1.3、散点曲线图...

Urho3D约定

Urho3D使用以下约定和原则&#xff1a; 左手坐标系。正X、Y和Z轴指向右侧、上方和前方&#xff0c;正旋转为顺时针。度用于角度。顺时针顶点定义正面。音频音量指定为0.0&#xff08;静音&#xff09;到1.0&#xff08;全音量&#xff09;路径名使用斜杠而不是反斜杠。调用操作…...

python数据结构-列表,元组

列表 列表是Python中最通用的数据类型&#xff0c;可以写成方括号之间的逗号分隔值(项目)列表。 使用列表的重要事项是&#xff0c;列表中的项目不必是相同的类型。也就是说一个列表中的项目(元素)可以是数字&#xff0c;字符串&#xff0c;数组&#xff0c;字典等甚至是列表类…...

Properties类读配置文件、修改配置文件

Properties类简介(1)Properties类是专门用于读写配置文件的集合类(2)配置文件的后缀名为.properties,内容格式为:# 可以用“#”作为注释 键值 键值**注意:**键值对不需要有空格,值不需要用引号一起来。默认类型是String。键、值不可以是null(3)Properties类的方法可查找api文档…...

图解LeetCode——剑指 Offer 24. 反转链表

一、题目 定义一个函数&#xff0c;输入一个链表的头节点&#xff0c;反转该链表并输出反转后链表的头节点。 二、示例 示例: 【输入】 1->2->3->4->5->NULL 【输出】 5->4->3->2->1->NULL 限制&#xff1a; 0 < 节点个数 < 5000 三、…...

【C语言】“指针的运算”、“指针与数组”

文章目录一、指针运算1.指针 - 整数2.指针-指针3.指针关系运算二、指针与数组三、二级指针四、指针数组完结一、指针运算 指针可以进行整数&#xff0c;指针-指针&#xff0c;还有关系运算&#xff0c;其他的运算会被编译器阻止。 1.指针 - 整数 对指针进行的时候一定要注意不…...

Linux高级命令之查找文件命令

查找文件命令学习目标能够说出查找文件使用的命令1. find命令及选项的使用命令说明find在指定目录下查找文件(包括目录)find命令选项:选项说明-name根据文件名(包括目录名)字查找find命令及选项的效果图:2. find命令结合通配符的使用通配符:是一种特殊语句&#xff0c;主要有星…...

PyCharm+Docker:打造最舒适的深度学习炼丹炉

九、PyCharmDocker&#xff1a;打造最舒适的深度学习炼丹炉 安装docker&#xff1a; 如何在 Ubuntu 22.04 LTS 中安装 Docker 和 Docker Compose https://zhuanlan.zhihu.com/p/547169542 修改Linux硬盘卷标&#xff1a; ntfs文件系统&#xff1a;https://blog.csdn.net/n…...

【mock】手把手带你用mock写自定义接口+mock常用语法

mock自定义接口完整流程 官网语法规范:https://github.com/nuysoft/Mock/wiki/Syntax-Specification 首先: 要有一个项目,我这里是vue3项目,以下从vue3项目搭建开始,已搭建好的请直接看2 1.空目录下新建vue3项目 运行创建项目命令&#xff1a; 在bash中:(文件路径处输入cm…...

2023 年腾讯云服务器CVM快速配置购买教程,新手上云必备!

腾讯云服务器快速配置购买教程是新手必备的上云教程。主机教程网在本文中以腾讯云服务器为例&#xff0c;给大家带来一个完整的、手把手教学的服务器购买流程。助力快速完成服务器的购买、配置、以及网站的搭建&#xff0c;给新手节省宝贵的时间&#xff0c;避免采坑&#xff0…...

opencv显示图像

大家好&#xff0c;我是csdn的博主&#xff1a;lqj_本人 这是我的个人博客主页&#xff1a; lqj_本人的博客_CSDN博客-微信小程序,前端,python领域博主lqj_本人擅长微信小程序,前端,python,等方面的知识https://blog.csdn.net/lbcyllqj?spm1011.2415.3001.5343哔哩哔哩欢迎关注…...

C++:类和对象(中)

文章目录1 类的6个默认成员函数2 构造函数2.1 概念2.2 特性3 析构函数3.1 概念3.2 特性4 拷贝构造函数4.1 概念4.2 特性5 赋值运算符重载5.1 运算符重载5.2 赋值运算符重载5.3 前置重载和后置重载6 日期类的实现7 const成员8 取地址及const取地址操作符重载1 类的6个默认成员函…...

53. 最大子数组和

文章目录题目描述暴力法动态规划法分治法参考文献题目描述 给你一个整数数组 nums &#xff0c;请你找出一个具有最大和的连续子数组&#xff08;子数组最少包含一个元素&#xff09;&#xff0c;返回其最大和。 子数组 是数组中的一个连续部分。 示例 1&#xff1a; 输入&…...

基于Java+SpringBoot+SpringCloud+Vue前后端分离医院管理系统设计与实现

博主介绍&#xff1a;✌全网粉丝3W&#xff0c;全栈开发工程师&#xff0c;从事多年软件开发&#xff0c;在大厂呆过。持有软件中级、六级等证书。可提供微服务项目搭建、毕业项目实战、项目定制✌ 博主作品&#xff1a;《微服务实战》专栏是本人的实战经验总结&#xff0c;《S…...

QT基础入门【环境配置篇】linux桌面QT开发环境的构建以及问题解决

目录 一、下载QT的安装包 二、安装 1.执行以下命令开始安装 2.选择配置 三、启动...

Linux系统之部署企业内部静态导航页

Linux系统之部署企业内部静态导航页 一、本次实践目的二、检查本地系统环境1.检查系统版本2.检查内核版本三、下载静态导航页资源包1.创建下载目录2.下载资源包四、安装apache服务1.安装httpd2.复制网页文件3.重启httpd服务4.检查httpd服务状态五、访问导航页六、修改导航页网站…...

2023备战金三银四,Python自动化软件测试面试宝典合集(四)

接上篇&#xff1a;11、点击塞钱进红包&#xff0c;选择使用新卡付款&#xff0c;按照流程添加新卡&#xff0c;此时同样需要考虑金额>新卡余额&#xff0c;金额<新卡余额&#xff0c;金额新卡余额三种情况12、使用指纹确认付款(正确的/不正确的指纹)13、使用密码确认付款…...

算法训练营 day43 动态规划 不同路径 不同路径 II

算法训练营 day43 动态规划 不同路径 不同路径 II 不同路径 62. 不同路径 - 力扣&#xff08;LeetCode&#xff09; 一个机器人位于一个 m x n 网格的左上角 &#xff08;起始点在下图中标记为 “Start” &#xff09;。 机器人每次只能向下或者向右移动一步。机器人试图达…...

关联查询的SQL有几种情况

1、内连接&#xff1a;inner join … on 结果&#xff1a;A表 ∩ B表 2、左连接&#xff1a;A left join B on &#xff08;2&#xff09;A表全部 &#xff08;3&#xff09;A表- A∩B 3、右连接&#xff1a;A right join B on &#xff08;4&#xff09;B表全部 &#…...

查缺补漏三:事务隔离级别

什么是事务&#xff1f; 事务就是一组操作的集合&#xff0c;事务将整组操作作为一个整体&#xff0c;共同提交或者共同撤销 这些操作只能同时成功或者同时失败&#xff0c;成功即可提交事务&#xff0c;失败就执行事务回滚 MySQL的事务默认是自动提交的&#xff0c;一条语句执…...

没有她的通讯录(C语言实现)

&#x1f680;write in front&#x1f680; &#x1f4dd;个人主页&#xff1a;认真写博客的夏目浅石. &#x1f381;欢迎各位→点赞&#x1f44d; 收藏⭐️ 留言&#x1f4dd; &#x1f4e3;系列专栏&#xff1a;夏目的C语言宝藏 &#x1f4ac;总结&#xff1a;希望你看完之…...

Spring Security 从入门到精通

前言 Spring Security 是 Spring 家族中的一个安全管理框架。相比与另外一个安全框架Shiro&#xff0c;它提供了更丰富的功能&#xff0c;社区资源也比Shiro丰富。 一般来说中大型的项目都是使用SpringSecurity 来做安全框架。小项目有Shiro的比较多&#xff0c;因为相比与Spr…...

微信小程序Springboot vue停车场车位管理系统

系统分为用户和管理员两个角色 用户的主要功能有&#xff1a; 1.用户注册和登陆系统 2.用户查看系统的公告信息 3.用户查看车位信息&#xff0c;在线预约车位 4.用户交流论坛&#xff0c;发布交流信息&#xff0c;在线评论 5.用户查看地图信息&#xff0c;在线导航 6.用户查看个…...

看完这篇 教你玩转渗透测试靶机vulnhub——Hack Me Please: 1

Vulnhub靶机Hack Me Please: 1渗透测试详解Vulnhub靶机介绍&#xff1a;Vulnhub靶机下载&#xff1a;Vulnhub靶机安装&#xff1a;Vulnhub靶机漏洞详解&#xff1a;①&#xff1a;信息收集&#xff1a;②&#xff1a;漏洞利用③&#xff1a;获取反弹shell&#xff1a;④&#x…...

nodejs+vue地铁站自动售票系统-火车票售票系统vscode

地铁站自动售票系统主要包括个人中心、地铁线路管理、站点管理、购票信息管理、乘坐管理、用户信息管理等多个模块。它使用的是前端技术&#xff1a;nodejsvueelementui 前后端通讯一般都是采取标准的JSON格式来交互。前端技术&#xff1a;nodejsvueelementui,视图层其实质就是…...

Spring Security in Action 第十二章 OAuth 2是如何工作的?

本专栏将从基础开始&#xff0c;循序渐进&#xff0c;以实战为线索&#xff0c;逐步深入SpringSecurity相关知识相关知识&#xff0c;打造完整的SpringSecurity学习步骤&#xff0c;提升工程化编码能力和思维能力&#xff0c;写出高质量代码。希望大家都能够从中有所收获&#…...

天工开物 #5 我的 Linux 开发机

首先说一下结论&#xff1a;最终我选择了基于 Arch Linux[1] 的 Garuda Linux[2] 发行版作为基础来搭建自己的 Linux 开发机。Neofetch 时刻发行版的选择在上周末的这次折腾里&#xff0c;我一共尝试了 Garuda Linux 发行版&#xff0c;原教旨的 Arch Linux 发行版&#xff0c;…...

【沁恒WCH CH32V307V-R1开发板输出DAC实验】

【沁恒WCH CH32V307V-R1开发板输出DAC实验】1. 前言2. 软件配置2.1 安装MounRiver Studio3. DAC项目测试3.1 打开DAC工程3.2 编译项目4. 下载验证4.1 接线4.2 演示效果5. 小结1. 前言 数字/模拟转换模块&#xff08;DAC&#xff09;&#xff0c;包含 2 个可配置 8/12 位数字输入…...

Linux进程控制详解

目录前言一、进程创建1.1 fork函数初识1.2 写时拷贝1.3 fork常规用法1.4 fork调用失败的原因二、进程终止2.1 进程终止时&#xff0c;操作系统做了什么&#xff1f;&#xff1f;2.2 进程终止的常见方式有哪些&#xff1f;&#xff1f;2.3 如何用代码终止一个进程三、进程等待3.…...

网站建设及域名/如何写软文推广产品

根据SqlServer2000 Analysis Services提供的帮助材料展开&#xff0c;略作说明并且根据各个函数的侧重点编写相应的MDX&#xff1b;这些成果主要源于我的老大浩哥&#xff0c;再次向他表示感谢。数组函数 函数 描述 SetToArray 将一个或多个集合转换为数组&#xff0c;以用于…...

东营可以做网站的公司在哪/新产品推广

gradle构建工具学习系列 参考官方文档&#xff1a;Building Java Applications Sample 创建一个demo名称的目录。进入该目录中打开命令行窗口&#xff0c;输入 gradle init并依次选择6 1 1&#xff0c;项目名称和资源包名称都默认。 创建项目&#xff1a; 将该项目导入到ID…...

西华县住房和城乡建设局网站/网赌怎么推广拉客户

SQL存储过程基础语法之一 CREATE PROCEDURE创建存储过程&#xff0c;存储过程是保存起来的可以接受和返回用户提供的参数的 Transact-SQL 语句的集合。可以创建一个过程供永久使用&#xff0c;或在一个会话中临时使用&#xff08;局部临时过程&#xff09;&#xff0c;或在所有…...

织梦做英文网站/青岛网络优化代理

输入vim /etc/sysconfig/network-scripts/ifcfg-eth0命令&#xff0c;编辑该文件的信息&#xff1a; DEVICE"eth0" #IPV6INIT"yes" #从dhcp改成static BOOTPROTO"static" UUID"*********" #从no改成yes ONBOOT"yes" #这…...

如何查询网站空间商/手游推广代理平台有哪些

今天介绍一下lamp环境的配置。 服务器用的是阿里云的服务器。 L&#xff1a;centos、A&#xff1a;apache、M&#xff1a;mysql、P&#xff1a;php &#xff08;一&#xff09;安装apache 我们这里安装的是httpd &#xff08;1&#xff09;安装httpd # yum install httpd…...

北京的网站建设公司有哪些/苏州网站制作开发公司

谷歌于2015年正式推出的Kubernetes开源项目目前已经吸引了众多IT公司的关注&#xff0c;这些公司包括Redhat、CoreOS、IBM、惠普等知名IT公司&#xff0c;也包括国内如华为、时速云等公司。为什么Kubernetes会引发这么多公司的关注&#xff1f;最根本的原因是Kubernetes是新一代…...