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. 从1开始打印整数
  2. 线程t1, 打印非5的倍数
  3. 线程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仅仅是模拟线程调度不大一样了。

再回过头来看看代码哪些地方有问题:

  1. cond.notify_one(); count是5的倍数时,t1会通过notify_one通知t2做事,但并不会阻止t1继续执行。想想一下如果t1执行的很快而t2一直没得到调度,则t1会打印1,2,3,4,6,7,8,9,11...
  2. cond.wait(lock); 可能会假唤醒,此时t1并没有通知它。

那“this is fun2,count=6” 是怎么回事哪?不应该是5吗?一种可能性是(可以通过GDB调试来模拟):

条件变量注意事项

  1. 条件变量不擅长单打独斗,一般要和flag变量与锁同时使用。
  2. 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测试一下。

啰嗦几句多线程程序的测试

多线程程序架构设计很重要,因为它很难测试,很难穷尽负面测试用例。几种可行的测试办法:

  1. 随机加sleep。需要改程序。参考上面。
  2. GDB调试。模拟和正常运行不同的调度策略。参考《GDB调试技巧实战--多线程&弱鸡条件变量-CSDN博客》
  3. strace、bpftrace、bcc把快速运行的程序降慢。目的是降低一个线程的速度,另一个保持原来的速度或者也降低。原来两者都是100迈前进,那一个100另一个50会不会出问题?或者10 20哪?组合就很多了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/643796.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

React16源码: React中的renderRoot的错误处理的源码实现

renderRoot的错误处理 1 &#xff09;概述 在 completeWork这个方法之后, 再次回到 renderRoot 里面在 renderRoot 里面执行了 workLoop, 之后&#xff0c;对 workLoop 使用了try catch如果在里面有任何一个节点在更新的过程当中 throw Error 都会被catch到catch到之后就是错误…

安卓之热修复的原理以及解决方案

文章摘要 在当今快速迭代的应用开发环境中&#xff0c;热修复技术&#xff08;Hot Fix&#xff09;成为了一个重要的工具。特别是在Android平台上&#xff0c;热修复提供了一种在运行时修复应用程序缺陷的方法&#xff0c;而无需重新发布应用程序。本文将深入探讨安卓热修复的原…

Zabbix分布式监控系统

实验过程 ps&#xff1a; 阿里云盘Xnode1获取 xnode1 https://www.alipan.com/s/HgLXfoeBWG2 提取码: eb70 1、xnode1克隆两台虚拟机并修改ip zabbix-server192.168.224.3 zabbix-agent192.168.224.4 2、修改主机名 [rootlocalhost ~]# hostnamectl set-hostname zabbix-se…

Vue开始封装全局防抖和节流函数

封装文件 封装文件的实现思路如下&#xff1a; 首先&#xff0c;我们需要定义两个函数&#xff1a;防抖函数和节流函数。这两个函数的目的是为了减少频繁触发某个事件导致的性能问题&#xff1b;防抖函数的实现思路是创建一个计时器变量&#xff0c;用于延迟执行函数。当触发…

Spring Boot 初始(快速搭建 Spring Boot 应用环境)

提示&#xff1a; ① 通过下面的简介可以快速的搭建一个可以运行的 Spring Boot 应用&#xff08;估计也就2分钟吧&#xff09;&#xff0c;可以简单的了解运行的过程。 ② 建议还是有一点 Spring 和 SpringMVC的基础&#xff08;其实搭建一个 Spring Boot 环境不需要也没有关系…

uniapp中打包Andiord app,在真机调试时地图以及定位功能可以正常使用,打包成app后失效问题(高德地图)

踩坑uniapp中打包Andiord app&#xff0c;在真机调试时地图以及定位功能可以正常使用&#xff0c;打包成app后失效问题_uniapp真机调试高德地图正常 打包apk高德地图就不加载-CSDN博客 问题&#xff1a; 目前两个项目&#xff0c;一个项目是从另一个项目里面分割出来的一整套…

AI 赋能绿色制冷,香港岭南大学开发 DEMMFL 模型进行建筑冷负荷预测

近年来&#xff0c;城市化进程加速所带来的碳排放量骤增&#xff0c;已经严重威胁到了全球环境。多个国家均已给出了「碳达峰&#xff0c;碳中和」的明确时间点&#xff0c;一场覆盖全球、全行业的「绿色革命」已经拉开序幕。在一众行业中&#xff0c;建筑是当之无愧的能耗大户…

15 # 类型检查机制:类型推断

类型检查机制 类型检查机制&#xff1a;TypeScript 编译器在做类型检查时&#xff0c;所秉承的一些原则&#xff0c;以及表现出的一些行为。 作用&#xff1a;辅助开发&#xff0c;提高开发效率。 类型推断类型兼容性类型保护 类型推断 不需要指定变量的类型&#xff08;函…

初识node.js(使用)

文章目录 项目目录介绍和运行流程1.index.html&#x1f447;2.整个项目的核心入口文件其实是main.js3.App.vue 组件化开发 和 根组件普通组件的注册1.局部注册2.全局注册 综合案例 项目目录介绍和运行流程 1.index.html&#x1f447; <!DOCTYPE html> <html lang&quo…

Android 13.0 去掉音量键电源键组合键的屏幕截图功能

1.概述 在13.0的产品rom定制化开发中,系统默认可以通过音量键和电源键来截图的,但是产品不需要截图功能,所以要求去掉音量和电源键的截图功能,所以要分析组合键截图功能屏蔽掉就好了 2.去掉音量键电源键组合键的屏幕截图功能的核心代码 frameworks/base/services/core/j…

Kafka集群的安装与配置(二)

2.2.2 生产者命令行操作 1 &#xff09;查看操作生产者命令参数 [atguiguhadoop102 kafka]$ bin/ kafka-console-producer.sh 2 &#xff09;发送消息 [atguiguhadoop102 kafka]$ bin/kafka-console-producer.sh --bootstrap-server hadoop102:9092 --topic first >h…

宠物互联网医院系统

在数字时代&#xff0c;宠物医疗迎来了一场革新&#xff0c;动物互联网医院系统以其先进的技术和智能的特性成为宠物护理的领军者。本文将介绍宠物互联网医院系统的一些关键技术和代码示例&#xff0c;揭示这一科技奇迹的实现原理。 1. 远程医疗服务的实现 远程医疗服务是宠…

国标GB28181协议EasyCVR启动失败报错“Local Machine Check Error”的解决方法

国标GB28181安防监控系统EasyCVR平台采用了开放式的网络结构&#xff0c;可支持4G、5G、WiFi、有线等方式进行视频的接入与传输、处理和分发。安防视频监控平台EasyCVR还能支持GIS电子地图模式&#xff0c;基于监控摄像头的经纬度地理位置信息&#xff0c;将场景中的整体安防布…

当pytest遇上poium会擦出什么火花

当pytest遇上poium会擦出什么火花 首先&#xff0c;创建一个test_sample/test_demo.py 文件&#xff0c;写入下面三行代码。 def test_bing(page):page.get("https://www.bing.com")assert page.get_title "必应"不要问题 page 从哪里来&#xff0c;打开…

什么是lustre文件系统

参考&#xff1a; https://blog.csdn.net/weixin_43912621/article/details/134215133 Lustre架构是用于集群的存储架构。Lustre架构的核心组件是Lustre文件系统&#xff0c;它在Linux操作系统上得到支持&#xff0c;并提供了一个符合POSIX *标准的UNIX文件系统接口。 Lustre…

浅谈DNS的工作原理及其作用

DNS&#xff0c;全称为Domain Name System&#xff0c;即域名系统&#xff0c;是一种用于将域名和IP地址相互映射的分布式数据库系统。它将可读的域名转换为对应的IP地址&#xff0c;使得用户可以更方便地通过域名来访问网络上的资源。今天锐成就简单探讨一下DNS的工作原理及其…

Hudi0.14.0 集成 Spark3.2.3(IDEA编码方式)

本次在IDEA下使用Scala语言进行开发,具体环境搭建查看文章 IDEA 下 Scala Maven 开发环境搭建。 1 环境准备 1.1 添加maven依赖 创建Maven工程,pom文件: <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apa…

数据采集与预处理02 :网络爬虫实战

数据采集与预处理02 &#xff1a;网络爬虫实战 爬虫基本知识 1 HTTP的理解 URL uniform resource locator. 是统一资源定位符&#xff0c;URI identifier是统一资源标识符。几乎所有的URI都是URL。 URL前部一般可以看到是HTTP还是HTTPS&#xff0c; 这是访问资源需要的协议…

Kafka-服务端-KafkaController

Broker能够处理来自KafkaController的LeaderAndIsrRequest、StopReplicaRequest、UpdateMetadataRequest等请求。 在Kafka集群的多个Broker中&#xff0c;有一个Broker会被选举为Controller Leader,负责管理整个集群中所有的分区和副本的状态。 例如&#xff1a;当某分区的Le…

使用Electron打包vue文件变成exe应用程序

文章目录 一、下载Electron二、修改下载的Electron项目1.修改index.html文件2.修改main.js文件3.修改package.json文件 三、修改vue项目1.修改vite.config.js文件2.修改.env.production文件3.修改auth.js文件4.修改router下得index.js文件6.修改Navbar.vue文件 四、Electron打包…