Modern C++ 一个例子学习条件变量
目录
问题程序
施魔法让BUG浮出水面
条件变量注意事项
修改程序
问题程序
今天无意中看到一篇帖子,关于条件变量的,不过仔细看看发现它并达不到原本的目的。
程序如下,读者可以先想想他的本意,以及有没有问题:
#include <iostream>
#include <thread>
#include <condition_variable>
#include <mutex>
#include <unistd.h>
using namespace std;
//全局条件变量
condition_variable cond;
mutex _mutex;
int count = 0;void fun1(){while(1){count++;unique_lock<mutex>lock(_mutex);if(count%5 == 0){cond.notify_one();}else{cout<<"this is fun1,count="<<count<<endl;}lock.unlock();sleep(1);}
}void fun2()
{while(1){unique_lock<mutex>lock(_mutex);cond.wait(lock);cout<<"this is fun2,count="<<count<<endl;lock.unlock();sleep(2);}
}int main()
{thread t1(fun1);thread t2(fun2);t1.join();t2.join();return 0;
}
OK,本意显然是:
- 从1开始打印整数
- 线程t1, 打印非5的倍数
- 线程t2, 打印5的倍数
编译执行,运行的还不错,符合预期,但这都是sleep的功劳。
施魔法让BUG浮出水面
把fun1中的sleep去掉,fun2中的sleep放到cond.wait(lock)后,它BUG的面目就暴露出来了:
void fun1(){while(1){count++;unique_lock<mutex>lock(_mutex);if(count%5 == 0){cond.notify_one();}else{cout<<"this is fun1,count="<<count<<endl;}lock.unlock();}
}void fun2()
{while(1){unique_lock<mutex>lock(_mutex);cond.wait(lock);sleep(2);cout<<"this is fun2,count="<<count<<endl;lock.unlock();}
}
[mzhai@lock]$ ./a.out this is fun1,count=1 this is fun1,count=2 this is fun1,count=3 this is fun1,count=4 this is fun2,count=6 this is fun1,count=6 this is fun1,count=7 this is fun1,count=8 this is fun1,count=9 this is fun1,count=11 this is fun1,count=12 this is fun1,count=13 this is fun1,count=14 this is fun1,count=16 this is fun1,count=17 this is fun1,count=18 this is fun1,count=19 this is fun1,count=21
多线程结果不能因随机加了几个sleep就不同,加sleep仅仅是模拟线程调度不大一样了。
再回过头来看看代码哪些地方有问题:
- cond.notify_one(); count是5的倍数时,t1会通过notify_one通知t2做事,但并不会阻止t1继续执行。想想一下如果t1执行的很快而t2一直没得到调度,则t1会打印1,2,3,4,6,7,8,9,11...
- cond.wait(lock); 可能会假唤醒,此时t1并没有通知它。
那“this is fun2,count=6” 是怎么回事哪?不应该是5吗?一种可能性是(可以通过GDB调试来模拟):
条件变量注意事项
- 条件变量不擅长单打独斗,一般要和flag变量与锁同时使用。
- notify对方线程并不代表调度让给了对方线程。
修改程序
说了那么多,怎么改哪?
这是一个典型的你等我我等你的例子,对于这个例子都是一方干完事情另一方才能继续,完全串休化的任务,直接写到一个线程里即可。如果说我为了练习线程同步技巧非要整两个线程,那也行,condition_variable官方文档上就有一个例子实现了main线程等待worker_thread完成任务:
#include <condition_variable>
#include <iostream>
#include <mutex>
#include <string>
#include <thread>std::mutex m;
std::condition_variable cv;
std::string data;
bool ready = false;
bool processed = false;void worker_thread()
{// Wait until main() sends datastd::unique_lock lk(m);cv.wait(lk, []{ 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)lk.unlock();cv.notify_one();
}int main()
{std::thread worker(worker_thread);data = "Example data";// send data to the worker thread{std::lock_guard lk(m);ready = true;std::cout << "main() signals data ready for processing\n";}cv.notify_one();// wait for the worker{std::unique_lock lk(m);cv.wait(lk, []{ return processed; });}std::cout << "Back in main(), data = " << data << '\n';worker.join();
}
我们依样画葫芦:
#include <iostream>
#include <thread>
#include <condition_variable>
#include <mutex>
#include <unistd.h>
using namespace std;
//全局条件变量
condition_variable cond;
mutex _mutex;
bool ready = false;
bool processed = false;int count = 0;void fun1(){while(1){count++;unique_lock<mutex> lock1(_mutex);if(count%5 == 0){ready = true;processed = false;lock1.unlock();cond.notify_one();lock1.lock();cond.wait(lock1, []{ return processed; });}else{cout<<"this is fun1,count="<<count<<endl;}lock1.unlock();}
}void fun2()
{while(1){unique_lock<mutex> lock1(_mutex);cond.wait(lock1, []{ return ready; });cout<<"this is fun2,count="<<count<<endl;processed = true;ready = false;lock1.unlock();cond.notify_one();}
}int main()
{thread t1(fun1);thread t2(fun2);t1.join();t2.join();return 0;
}
结果符合预期,感兴趣的读者可以到处插入sleep测试一下。
啰嗦几句多线程程序的测试
多线程程序架构设计很重要,因为它很难测试,很难穷尽负面测试用例。几种可行的测试办法:
- 随机加sleep。需要改程序。参考上面。
- GDB调试。模拟和正常运行不同的调度策略。参考《GDB调试技巧实战--多线程&弱鸡条件变量-CSDN博客》
- strace、bpftrace、bcc把快速运行的程序降慢。目的是降低一个线程的速度,另一个保持原来的速度或者也降低。原来两者都是100迈前进,那一个100另一个50会不会出问题?或者10 20哪?组合就很多了。
相关文章:

Modern C++ 一个例子学习条件变量
目录 问题程序 施魔法让BUG浮出水面 条件变量注意事项 修改程序 问题程序 今天无意中看到一篇帖子,关于条件变量的,不过仔细看看发现它并达不到原本的目的。 程序如下,读者可以先想想他的本意,以及有没有问题: #…...

ora-12154无法解析指定的连接标识符
用户反映查询的时候报错ora-12154 这个系统只做历史数据查询使用,使用并不平凡,该数据库曾做过一次服务器间的迁移。 用户描述,所有oracle客户端查询该视图都报tns错误,一般ora-12154会发生在连接数据库时,因为tns配…...

rust跟我学三:文件时间属性获得方法
图为RUST吉祥物 大家好,我是get_local_info作者带剑书生,这里用一篇文章讲解get_local_info是怎样获得杀毒软件的病毒库时间的。 首先,先要了解get_local_info是什么? get_local_info是一个获取linux系统信息的rust三方库,并提供一些常用功能,目前版本0.2.4。详细介绍地址…...

解决一个mysql的更新属性长度问题
需求背景: 线上有一个 platform属性,原有长度为 varchar(10),但是突然需要填入一个11位长度的值;而偏偏这个属性在线上100张表中有50张都存在,并且名字各式各样,庆幸都包含 platform;例如 platf…...
[网络安全]DHCP 部署与安全
一 、DHCP作用 (Dynamic HOst Configure Protocol ) 动态IP配置协议 作用:动态自动分配IP地址 二、DHCP相关概念 地址池/作用域: (IP、子网掩码、网关、DNS、周期) 三、DHCP优点 减少工程量 避免IP避免 提高地址利用率 四、DHCP原理 成为DHCP租约过程 步骤: 1.发送 DHC…...
自建ES集群
常用命令 # 重命名文件夹 mv elasticsearch-7.10.2 elasticsearch# 移动文件到文件夹 mv elasticsearch-7.10.2-linux-x86_64.tar.gz middleware-tar/ mv kibana-7.10.2-linux-x86_64.tar.gz middleware-tar/# 创建data文件夹 mkdir /home/admin/elasticsearch/data 自建Ela…...
git rev-parse v406 ‘v4.0.4‘^{} master什么意思?
git rev-parse 是一个 Git 命令,用于解析出 git 对象(如分支、标签、提交等)的完整 SHA-1 哈希值。这个命令对于理解 git 中各种引用的内部表示非常有用。 让我们一步步分析 git rev-parse v406 v4.0.4^{} master 这条命令: v406…...

AI 编程的机会和未来:从 Copilot 到 Code Agent
大模型的快速发展带来了 AI 应用的井喷。统计 GPT 使用情况,编程远超其他成为落地最快、使用率最高的场景。如今,大量程序员已经习惯了在 AI 辅助下进行编程。数据显示,GitHub Copilot 将程序员工作效率提升了 55%,一些实验中 AI …...
git push --set-upstream origin master时超时失败的解决方案
问题描述 提示:这里描述项目中遇到的问题: git push --set-upstream origin master时,超时失败,显示如下错误: connect to host git.acwing.com port 22: Connection timed out fatal: Could not read from remote …...
beego的模块篇 - config自定义文件配置
加载自定义配置到beego.AppConfig中可以配置:Beego框架 app.conf配置参数及环境配置-CSDN博客 1. 文件配置 目前支持解析的文件格式有 ini、json、xml、yaml 安装依赖库: go get github.com/beego/beego/v2/core/config 1.1 ini文件配置使用 配置文…...

YOLOv5-第Y2周:训练自己的数据集
YOLOv5-第Y2周:训练自己的数据集 YOLOv5-第Y2周:训练自己的数据集一、前言二、我的环境三、准备数据集四、运行 split_train_val.py 文件五、生成 train.txt、test.txt、val.txt 文件六、创建ab.yaml文件七、开始使用自己的数据集训练八、总结 YOLOv5-第…...
解决fxml图标无法显示
原文地址:https://www.myjinji.top/articles/2023/10/11/1697033367492.html 代码正确无法显示 <Button fx:id"blockButton" onAction"#handleBlockButtonClick"><graphic><FontIcon iconLiteral"win10-add-shopping-cart…...

React Store及store持久化的使用
1.安装 npm insatll react-redux npm install reduxjs/toolkit npm install redux-persist2. 使用React Toolkit创建counterStore并配置持久化 store/modules/counterStore.ts: import { createSlice } from reduxjs/toolkit// 定义状态类型 interface Action {…...
Hive添加第三方Jar包方式总结
一、在 Hive Shell中加入—add jar hdfs dfs -put HelloUDF-1.0.jar /tmp beeline -u "jdbc:hive2://test.bigdata.com:10000" -n "song" -p "" add jar hdfs:///tmp/HelloUDF-1.0.jar; create function HelloUDF as org.example.HelloUDF USIN…...

Linux用户与文件的关系和文件掩码(umask)的作用
文章目录 1 前言2 Linux用户与文件的关系3 文件掩码(umask)4 总结 1 前言 阅读本篇文章,你将了解Linux的目录结构,用户与文件的关系,以及文件掩码的作用。为了方便大家理解,本文将通过实例进行演示…...
JS -- 正则表达式教程
1 概念 ECMAScript 通过 RegExp 类型支持正则表达式。 2 写法 2.1 类似 Perl 的简写语法: let pattern /a/g let pattern2 /a/i2.2 构造函数创建: let pattern new RegExp(a, g) let pattern new RegExp(a, i)上面两种是等价的正则表达式 3 修…...

详细介绍IP 地址、网络号和主机号、ABC三类、ip地址可分配问题、子网掩码、子网划分
1、 IP 地址: 网络之间互连的协议,是由4个字节(32位二进制)组成的逻辑上的地址。 将32位二进制进行分组,分成4组,每组8位(1个字节)。【ip地址通常使用十进制表示】ip地址分成四组之后,在逻辑上,分成网络号和主机号 2…...

滚动菜单+图片ListView
目录 Fruit.java FruitAdapter MainActivity activity_main.xml fruit.xml 整体结构 Fruit.java public class Fruit {private String name;private int imageId;public Fruit(String name, int imageId) {this.name name;this.imageId imageId;}public String getNam…...

【4k】4k的webrtc播放示例
目录 使用带研发角色的账号,在app端设置下分辨率 : 4k 点播 ffplay播放看下详细的参数 使用带研发角色的账号,在app端设置下分辨率 : 4k 点播 ffplay播放看下详细的参数...

PMIC 基础知识浅析(四)
PMIC 后端研究现状: 现今针对便携式移动平台的电源管理芯片仍以传统分离型 PMIC为主。 根据后端设计的特点,传统分离型 PMIC 又可分三大类。 控制芯片与开关 MOSFET 分离型,MOSFET 外置于PCB上,芯片仅提供智能控制功能。 此类IC…...

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)
题目:3442. 奇偶频次间的最大差值 I 思路 :哈希,时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况,哈希表这里用数组即可实现。 C版本: class Solution { public:int maxDifference(string s) {int a[26]…...

边缘计算医疗风险自查APP开发方案
核心目标:在便携设备(智能手表/家用检测仪)部署轻量化疾病预测模型,实现低延迟、隐私安全的实时健康风险评估。 一、技术架构设计 #mermaid-svg-iuNaeeLK2YoFKfao {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg…...

【机器视觉】单目测距——运动结构恢复
ps:图是随便找的,为了凑个封面 前言 在前面对光流法进行进一步改进,希望将2D光流推广至3D场景流时,发现2D转3D过程中存在尺度歧义问题,需要补全摄像头拍摄图像中缺失的深度信息,否则解空间不收敛…...
今日科技热点速览
🔥 今日科技热点速览 🎮 任天堂Switch 2 正式发售 任天堂新一代游戏主机 Switch 2 今日正式上线发售,主打更强图形性能与沉浸式体验,支持多模态交互,受到全球玩家热捧 。 🤖 人工智能持续突破 DeepSeek-R1&…...
Pinocchio 库详解及其在足式机器人上的应用
Pinocchio 库详解及其在足式机器人上的应用 Pinocchio (Pinocchio is not only a nose) 是一个开源的 C 库,专门用于快速计算机器人模型的正向运动学、逆向运动学、雅可比矩阵、动力学和动力学导数。它主要关注效率和准确性,并提供了一个通用的框架&…...

10-Oracle 23 ai Vector Search 概述和参数
一、Oracle AI Vector Search 概述 企业和个人都在尝试各种AI,使用客户端或是内部自己搭建集成大模型的终端,加速与大型语言模型(LLM)的结合,同时使用检索增强生成(Retrieval Augmented Generation &#…...

【VLNs篇】07:NavRL—在动态环境中学习安全飞行
项目内容论文标题NavRL: 在动态环境中学习安全飞行 (NavRL: Learning Safe Flight in Dynamic Environments)核心问题解决无人机在包含静态和动态障碍物的复杂环境中进行安全、高效自主导航的挑战,克服传统方法和现有强化学习方法的局限性。核心算法基于近端策略优化…...

WebRTC调研
WebRTC是什么,为什么,如何使用 WebRTC有什么优势 WebRTC Architecture Amazon KVS WebRTC 其它厂商WebRTC 海康门禁WebRTC 海康门禁其他界面整理 威视通WebRTC 局域网 Google浏览器 Microsoft Edge 公网 RTSP RTMP NVR ONVIF SIP SRT WebRTC协…...
【大厂机试题解法笔记】矩阵匹配
题目 从一个 N * M(N ≤ M)的矩阵中选出 N 个数,任意两个数字不能在同一行或同一列,求选出来的 N 个数中第 K 大的数字的最小值是多少。 输入描述 输入矩阵要求:1 ≤ K ≤ N ≤ M ≤ 150 输入格式 N M K N*M矩阵 输…...
rk3506上移植lvgl应用
本文档介绍如何在开发板上运行以及移植LVGL。 1. 移植准备 硬件环境:开发板及其配套屏幕 开发板镜像 主机环境:Ubuntu 22.04.5 2. LVGL启动 出厂系统默认配置了 LVGL,并且上电之后默认会启动 一个LVGL应用 。 LVGL 的启动脚本为/etc/init.d/pre_init/S00-lv_demo,…...