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…...
python打卡day49
知识点回顾: 通道注意力模块复习空间注意力模块CBAM的定义 作业:尝试对今天的模型检查参数数目,并用tensorboard查看训练过程 import torch import torch.nn as nn# 定义通道注意力 class ChannelAttention(nn.Module):def __init__(self,…...
Java 8 Stream API 入门到实践详解
一、告别 for 循环! 传统痛点: Java 8 之前,集合操作离不开冗长的 for 循环和匿名类。例如,过滤列表中的偶数: List<Integer> list Arrays.asList(1, 2, 3, 4, 5); List<Integer> evens new ArrayList…...
抖音增长新引擎:品融电商,一站式全案代运营领跑者
抖音增长新引擎:品融电商,一站式全案代运营领跑者 在抖音这个日活超7亿的流量汪洋中,品牌如何破浪前行?自建团队成本高、效果难控;碎片化运营又难成合力——这正是许多企业面临的增长困局。品融电商以「抖音全案代运营…...
2021-03-15 iview一些问题
1.iview 在使用tree组件时,发现没有set类的方法,只有get,那么要改变tree值,只能遍历treeData,递归修改treeData的checked,发现无法更改,原因在于check模式下,子元素的勾选状态跟父节…...
生成 Git SSH 证书
🔑 1. 生成 SSH 密钥对 在终端(Windows 使用 Git Bash,Mac/Linux 使用 Terminal)执行命令: ssh-keygen -t rsa -b 4096 -C "your_emailexample.com" 参数说明: -t rsa&#x…...
uniapp微信小程序视频实时流+pc端预览方案
方案类型技术实现是否免费优点缺点适用场景延迟范围开发复杂度WebSocket图片帧定时拍照Base64传输✅ 完全免费无需服务器 纯前端实现高延迟高流量 帧率极低个人demo测试 超低频监控500ms-2s⭐⭐RTMP推流TRTC/即构SDK推流❌ 付费方案 (部分有免费额度&#x…...
C++ 求圆面积的程序(Program to find area of a circle)
给定半径r,求圆的面积。圆的面积应精确到小数点后5位。 例子: 输入:r 5 输出:78.53982 解释:由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982,因为我们只保留小数点后 5 位数字。 输…...
Mac下Android Studio扫描根目录卡死问题记录
环境信息 操作系统: macOS 15.5 (Apple M2芯片)Android Studio版本: Meerkat Feature Drop | 2024.3.2 Patch 1 (Build #AI-243.26053.27.2432.13536105, 2025年5月22日构建) 问题现象 在项目开发过程中,提示一个依赖外部头文件的cpp源文件需要同步,点…...
FFmpeg:Windows系统小白安装及其使用
一、安装 1.访问官网 Download FFmpeg 2.点击版本目录 3.选择版本点击安装 注意这里选择的是【release buids】,注意左上角标题 例如我安装在目录 F:\FFmpeg 4.解压 5.添加环境变量 把你解压后的bin目录(即exe所在文件夹)加入系统变量…...
人工智能--安全大模型训练计划:基于Fine-tuning + LLM Agent
安全大模型训练计划:基于Fine-tuning LLM Agent 1. 构建高质量安全数据集 目标:为安全大模型创建高质量、去偏、符合伦理的训练数据集,涵盖安全相关任务(如有害内容检测、隐私保护、道德推理等)。 1.1 数据收集 描…...
