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

C++并发多线程--死锁问题及解决方法

1--死锁问题

        死锁问题:两个线程访问资源时加锁,但都需要对方的资源才能执行释放锁;

        代码实例:下面的代码中,当线程 1 使用 my_mutex1 上锁后,会继续使用 my_mutex2 进行上锁,若此时线程 2 已经使用 my_mutex2 上锁了,也准备继续使用 my_mytex1 上锁;这时就会出现死锁问题:线程 1 掌握 my_mutex1 的资源,需要使用 my_mutex2 的资源,而线程 2 掌握 my_mutex2 的资源,需要使用 my_mutex1 的资源;两个线程都掌握各自需要的资源,导致两个线程无法继续执行,产生了死锁;

        一个简单的解决方案:将两个线程的上锁顺序设计为相同;

#include <iostream>
#include <thread>
#include <list>
#include <mutex> // 引入互斥class myClass{
public:// 收集数据到消息队列void inMsgRecvQueue(){for(int i = 0; i < 100; i++){// 模拟收集消息std::cout << "Running inMsgRecvQueue(), insert one value: " << i << std::endl; my_mutex1.lock();my_mutex2.lock();msgRecvqueue.push_back(i); // 消息队列存储消息my_mutex2.unlock();my_mutex1.unlock();}}// 从消息队列取数据void outMsgRecvQueue(){for(int i = 0; i < 100; i++){if(!msgRecvqueue.empty()){my_mutex2.lock(); // 加锁my_mutex1.lock(); // 加锁// 取出数据int command = msgRecvqueue.front();msgRecvqueue.pop_front(); my_mutex1.unlock(); // 解锁my_mutex2.unlock(); // 解锁}else{std::cout << "Running outMsgRecvQueue(), " "the msgRecvqueue is empty" << std::endl;}}}
private:std::list<int> msgRecvqueue; // 消息队列std::mutex my_mutex1; // 创建互斥量 std::mutex my_mutex2; // 创建互斥量 
};int main(int argc, char *argv[]){myClass sample1;// 使用成员函数创建线程std::thread myInMsgObj(&myClass::inMsgRecvQueue, &sample1); // 收集数据线程std::thread myOutMsgObj(&myClass::outMsgRecvQueue, &sample1); // 取出数据线程myInMsgObj.join();myOutMsgObj.join();return 0;
}

2--std::lock( )函数模板

        std::lock() 可以锁住两个或两个以上的互斥量,可以避免因为互斥量上锁顺序不同而导致的死锁问题;

        std::lock() 一般用于处理多个互斥量,其要么将多个互斥量同时锁住,要么将多个互斥量解锁(当部分互斥量未上锁时,要么等待其余互斥量上锁才继续执行,要么释放已经锁住的互斥量);

        下面的实例代码使用 std::lock() 来解决上面的死锁问题;

#include <iostream>
#include <thread>
#include <list>
#include <mutex> // 引入互斥class myClass{
public:// 收集数据到消息队列void inMsgRecvQueue(){for(int i = 0; i < 100; i++){// 模拟收集消息std::cout << "Running inMsgRecvQueue(), insert one value: " << i << std::endl; std::lock(my_mutex1, my_mutex2);msgRecvqueue.push_back(i); // 消息队列存储消息my_mutex2.unlock();my_mutex1.unlock();}}// 从消息队列取数据void outMsgRecvQueue(){for(int i = 0; i < 100; i++){if(!msgRecvqueue.empty()){std::lock(my_mutex1, my_mutex2);// 取出数据int command = msgRecvqueue.front();msgRecvqueue.pop_front(); my_mutex1.unlock(); // 解锁my_mutex2.unlock(); // 解锁}else{std::cout << "Running outMsgRecvQueue(), " "the msgRecvqueue is empty" << std::endl;}}}
private:std::list<int> msgRecvqueue; // 消息队列std::mutex my_mutex1; // 创建互斥量 std::mutex my_mutex2; // 创建互斥量 
};int main(int argc, char *argv[]){myClass sample1;// 使用成员函数创建线程std::thread myInMsgObj(&myClass::inMsgRecvQueue, &sample1); // 收集数据线程std::thread myOutMsgObj(&myClass::outMsgRecvQueue, &sample1); // 取出数据线程myInMsgObj.join();myOutMsgObj.join();return 0;
}

3--std::lock_guard( )函数模板

        std::lock_guard( ) 可以替换 mutex.lock() 和 mutex.unlock(),在项目复杂的时候可以避免遗漏上锁和解锁的问题;

        std::lock_guard<std::mutex> 对象名(互斥量, std::adopt_lock); std::adopt_lock 的作用是表明互斥量在之前已经上锁了,无需额外对互斥量进行上锁操作,只需进行解锁操作即可;

#include <iostream>
#include <thread>
#include <list>
#include <mutex> // 引入互斥class myClass{
public:// 收集数据到消息队列void inMsgRecvQueue(){for(int i = 0; i < 100; i++){// 模拟收集消息std::cout << "Running inMsgRecvQueue(), insert one value: " << i << std::endl; std::lock(my_mutex1, my_mutex2);msgRecvqueue.push_back(i); // 消息队列存储消息std::lock_guard<std::mutex> guard1(my_mutex1, std::adopt_lock);std::lock_guard<std::mutex> guard2(my_mutex2, std::adopt_lock);}}// 从消息队列取数据void outMsgRecvQueue(){for(int i = 0; i < 100; i++){if(!msgRecvqueue.empty()){std::lock(my_mutex1, my_mutex2);// 取出数据int command = msgRecvqueue.front();msgRecvqueue.pop_front(); std::lock_guard<std::mutex> guard1(my_mutex1, std::adopt_lock);std::lock_guard<std::mutex> guard2(my_mutex2, std::adopt_lock);}else{std::cout << "Running outMsgRecvQueue(), " "the msgRecvqueue is empty" << std::endl;}}}
private:std::list<int> msgRecvqueue; // 消息队列std::mutex my_mutex1; // 创建互斥量 std::mutex my_mutex2; // 创建互斥量 
};int main(int argc, char *argv[]){myClass sample1;// 使用成员函数创建线程std::thread myInMsgObj(&myClass::inMsgRecvQueue, &sample1); // 收集数据线程std::thread myOutMsgObj(&myClass::outMsgRecvQueue, &sample1); // 取出数据线程myInMsgObj.join();myOutMsgObj.join();return 0;
}

相关文章:

C++并发多线程--死锁问题及解决方法

1--死锁问题 死锁问题&#xff1a;两个线程访问资源时加锁&#xff0c;但都需要对方的资源才能执行释放锁&#xff1b; 代码实例&#xff1a;下面的代码中&#xff0c;当线程 1 使用 my_mutex1 上锁后&#xff0c;会继续使用 my_mutex2 进行上锁&#xff0c;若此时线程 2 已经使…...

【Spring】纯注解开发

1、简介 在Spring3.0升级了纯注解开发模式&#xff0c;使用Java类来代替配置文件&#xff0c;开启了Spring快速开发赛道。 2、定义bean Component Service Controller Repository 3、纯注解开发 使用Configuration声明一个配置类&#xff0c;使用ComponentScan来扫描作为bea…...

【算法心得】正确估计dfs时间复杂度;剪枝优化不怕重构

https://leetcode.cn/problems/verbal-arithmetic-puzzle/ 这题看到题&#xff0c;“表达式中使用的不同字符数最大为 10”&#xff0c;就觉得dfs就完事了&#xff0c;最多不过10!&#xff0c;10!才1e6&#xff0c;1e7这样。如果字符再少点&#xff0c;6! 7! 8!的&#xff0c;…...

通过网关访问微服务,一次正常,一次不正常 (nacos配置的永久实例却未启动导致)

微服务直接访问没问题&#xff0c;通过网关访问&#xff0c;就一次正常访问&#xff0c;一次401错误&#xff0c;交替正常和出错 负载均衡试了 路由配置检查了 最后发现nacos下竟然有2个order服务实例&#xff0c;我明明只开启了一个呀 原来之前的8080端口微服务还残留&…...

div输入框的文字超过指定行数用省略号表示css

实现效果&#xff1a;超过四行用省略号表示 实现方法&#xff1a; .text{overflow: hidden;text-overflow: ellipsis;display: -webkit-box;-webkit-line-clamp: 4; // 自定义行数-webkit-box-orient: vertical; }...

STM32 F103C8T6学习笔记5:定时器输出不同占空比PWM驱动舵机旋转角度

现在学习使用STM32 F103C8T6的定时器PWM模式&#xff0c;使用PWM驱动舵机转动不同角度&#xff0c;文章提供源码&#xff0c;测试工程&#xff0c;测试动态效果图。 目录 基础原理&#xff1a; 实验目标&#xff1a; 测试视频结果&#xff1a; 测试工程下载&#xff1a; 基…...

液体神经网络:LNN是个啥概念?

一、说明 在在人工智能领域&#xff0c;神经网络已被证明是解决复杂问题的非常强大的工具。多年来&#xff0c;研究人员不断寻求创新方法来提高其性能并扩展其能力。其中一种方法是液体神经网络&#xff08;LNN&#xff09;的概念&#xff0c;这是一个利用动态计算功能的迷人框…...

开源数据库Mysql_DBA运维实战 (DCL/日志)

SQL&#xff08;Structured Query Language 即结构化查询语言&#xff09; a.DDL语句 数据库定义语言&#xff1a; 数据库&#xff0c;表&#xff0c;视图&#xff0c;索引&#xff0c;存储过程&#xff0c;函数&#xff0c;创建删除ALTER(CREATE DROP ALTER) b.DML语句 数…...

神经网络基础-神经网络补充概念-03-逻辑回归损失函数

概念 逻辑回归使用的损失函数通常是"对数损失"&#xff08;也称为"交叉熵损失"&#xff09;或"逻辑损失"。这些损失函数在训练过程中用于衡量模型预测与实际标签之间的差异&#xff0c;从而帮助模型逐步调整权重参数&#xff0c;以更好地拟合数…...

基于深度信念神经网络的矿石产量预测,基于DBN的矿石产量预测,DBN的详细原理

目录 背影 DBN神经网络的原理 DBN神经网络的定义 受限玻尔兹曼机(RBM) DBN的矿石产量预测 基本结构 主要参数 数据 MATALB代码 结果图 展望 背影 DBN是一种深度学习神经网络,拥有提取特征,非监督学习的能力,是一种非常好的分类算法,本文将DBN算法进行矿石产量预测 DB…...

JavaWeb-Filter过滤器

目录 Filter过滤器 1. Filter的生命周期 2.Filter的配置 3.拦截路径 4.拦截具体的使用 5.拦截方式配置&#xff08;资源被访问方式&#xff09; 6.FilterChain拦截链 Filter过滤器 filter是过滤器&#xff0c;相比于Servlet的发送请求&#xff0c;filter是用于拦截请求。…...

python如何实现1ms内触发两个接口请求

在Python中&#xff0c;可以通过多线程或者协程来实现1ms内触发两个接口请求。以下是两种方法的示例代码&#xff1a; 1.多线程实现&#xff1a; import threading import requestsdef send_request(url):response requests.get(url)print(response.text)# 创建两个线程&…...

深入解析路由与网络:网络的脉络

目录 路由 广域网 公网 外网 局域网 内网 以太网 Wi-Fi CDN IPv4和IPv6 IP地址分类 无类别域间路由&#xff08;CIDR&#xff09; 路由 路由是指在计算机网络中&#xff0c;将数据包从源地址传递到目标地址的过程。在一个复杂的网络中&#xff0c;数据包需要经过多…...

spring.HttpMessageNotReadableException: JSON parse error

实体类如下&#xff1a; Value public class Search{//搜索内容String value;//是否模糊搜索boolean fuzzy true; //其实这样写并不是“默认”模糊搜索&#xff0c;而是“一定是”模糊搜索 }spring.HttpMessageNotReadableException: JSON parse error: Cannot construct ins…...

安全中间件的设计思路和简单实践

rasp 的侵入式特性和拦截特性导致开发和运维普通不太愿意配合&#xff0c;当生产环境出现问题时往往第一时间先把责任推给 rasp&#xff0c;逐渐的安全部门普遍只能把 rasp 设置为告警模式&#xff0c;而且越是大的集群拦截开的就越少&#xff0c;所以字节的 elkeid 和某外卖大…...

试卷扫描成电子版方法分享,这个方法不要错过

很多时候&#xff0c;为了方便传输我们需要将试卷扫描成电子版进行存档&#xff0c;以备不时之需。很多小伙伴如果遇到试卷需要扫描转成电子版可能就不知道该如何操作了&#xff0c;其实试卷扫描是一项非常重要的工作&#xff0c;因此需要注意一些方法和细节。以下是试卷扫描成…...

【PostgreSQL的CLOG解析】

同样还是这张图&#xff0c;之前发过shared_buffer和os cache、wal buffer和work mem的文章&#xff0c;今天的主题是图中的clog&#xff0c;即 commit log&#xff0c;PostgreSQL10之前放在数据库目录的pg_clog下面。PostgreSQL10之后修更名为xact,数据目录变更为pg_xact下面&…...

腾讯云国际站代充-阿里云ECS怎么一键迁移到腾讯云cvm?

今天主要来介绍一下如何通过阿里云国际ECS控制台一键迁移至腾讯云国际CVM。腾讯云国际站云服务器CVM提供全面广泛的服务内容。无-需-绑-定PayPal&#xff0c;代-充-值腾讯云国际站、阿里云国际站、AWS亚马逊云、GCP谷歌云&#xff0c;官方授权经销商&#xff01;靠谱&#xff0…...

东方晶源亮相第十一届半导体设备年会,共话发展“芯”机遇

8月11日&#xff0c;以“协力同芯抢机遇&#xff0c;集成创新造设备”为主题的第十一届&#xff08;2023年&#xff09;中国电子专用设备工业协会半导体设备年会暨产业链合作论坛&#xff08;CSEAC&#xff09;在无锡太湖国际博览中心圆满闭幕。为期3天的CSEAC&#xff0c;通过…...

git修改历史commit信息

修改历史提交 commit 的信息 操作步骤&#xff1a; git rebase -i 列出 commit 列表找到需要修改的 commit 记录&#xff0c;把 pick 修改为 edit 或 e&#xff0c;:wq 保存退出修改 commit 的具体信息git commit --amend&#xff0c;保存并继续下一条git 4. 4. rebase --cont…...

测试微信模版消息推送

进入“开发接口管理”--“公众平台测试账号”&#xff0c;无需申请公众账号、可在测试账号中体验并测试微信公众平台所有高级接口。 获取access_token: 自定义模版消息&#xff1a; 关注测试号&#xff1a;扫二维码关注测试号。 发送模版消息&#xff1a; import requests da…...

Python:操作 Excel 折叠

💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 Python 操作 Excel 系列 读取单元格数据按行写入设置行高和列宽自动调整行高和列宽水平…...

相机Camera日志实例分析之二:相机Camx【专业模式开启直方图拍照】单帧流程日志详解

【关注我&#xff0c;后续持续新增专题博文&#xff0c;谢谢&#xff01;&#xff01;&#xff01;】 上一篇我们讲了&#xff1a; 这一篇我们开始讲&#xff1a; 目录 一、场景操作步骤 二、日志基础关键字分级如下 三、场景日志如下&#xff1a; 一、场景操作步骤 操作步…...

QMC5883L的驱动

简介 本篇文章的代码已经上传到了github上面&#xff0c;开源代码 作为一个电子罗盘模块&#xff0c;我们可以通过I2C从中获取偏航角yaw&#xff0c;相对于六轴陀螺仪的yaw&#xff0c;qmc5883l几乎不会零飘并且成本较低。 参考资料 QMC5883L磁场传感器驱动 QMC5883L磁力计…...

解决Ubuntu22.04 VMware失败的问题 ubuntu入门之二十八

现象1 打开VMware失败 Ubuntu升级之后打开VMware上报需要安装vmmon和vmnet&#xff0c;点击确认后如下提示 最终上报fail 解决方法 内核升级导致&#xff0c;需要在新内核下重新下载编译安装 查看版本 $ vmware -v VMware Workstation 17.5.1 build-23298084$ lsb_release…...

C++ 基础特性深度解析

目录 引言 一、命名空间&#xff08;namespace&#xff09; C 中的命名空间​ 与 C 语言的对比​ 二、缺省参数​ C 中的缺省参数​ 与 C 语言的对比​ 三、引用&#xff08;reference&#xff09;​ C 中的引用​ 与 C 语言的对比​ 四、inline&#xff08;内联函数…...

代理篇12|深入理解 Vite中的Proxy接口代理配置

在前端开发中,常常会遇到 跨域请求接口 的情况。为了解决这个问题,Vite 和 Webpack 都提供了 proxy 代理功能,用于将本地开发请求转发到后端服务器。 什么是代理(proxy)? 代理是在开发过程中,前端项目通过开发服务器,将指定的请求“转发”到真实的后端服务器,从而绕…...

在QWebEngineView上实现鼠标、触摸等事件捕获的解决方案

这个问题我看其他博主也写了&#xff0c;要么要会员、要么写的乱七八糟。这里我整理一下&#xff0c;把问题说清楚并且给出代码&#xff0c;拿去用就行&#xff0c;照着葫芦画瓢。 问题 在继承QWebEngineView后&#xff0c;重写mousePressEvent或event函数无法捕获鼠标按下事…...

【Android】Android 开发 ADB 常用指令

查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...

Ubuntu Cursor升级成v1.0

0. 当前版本低 使用当前 Cursor v0.50时 GitHub Copilot Chat 打不开&#xff0c;快捷键也不好用&#xff0c;当看到 Cursor 升级后&#xff0c;还是蛮高兴的 1. 下载 Cursor 下载地址&#xff1a;https://www.cursor.com/cn/downloads 点击下载 Linux (x64) &#xff0c;…...