当前位置: 首页 > 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…...

AI-调查研究-01-正念冥想有用吗?对健康的影响及科学指南

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; &#x1f680; AI篇持续更新中&#xff01;&#xff08;长期更新&#xff09; 目前2025年06月05日更新到&#xff1a; AI炼丹日志-28 - Aud…...

Ubuntu系统下交叉编译openssl

一、参考资料 OpenSSL&&libcurl库的交叉编译 - hesetone - 博客园 二、准备工作 1. 编译环境 宿主机&#xff1a;Ubuntu 20.04.6 LTSHost&#xff1a;ARM32位交叉编译器&#xff1a;arm-linux-gnueabihf-gcc-11.1.0 2. 设置交叉编译工具链 在交叉编译之前&#x…...

Appium+python自动化(十六)- ADB命令

简介 Android 调试桥(adb)是多种用途的工具&#xff0c;该工具可以帮助你你管理设备或模拟器 的状态。 adb ( Android Debug Bridge)是一个通用命令行工具&#xff0c;其允许您与模拟器实例或连接的 Android 设备进行通信。它可为各种设备操作提供便利&#xff0c;如安装和调试…...

《Playwright:微软的自动化测试工具详解》

Playwright 简介:声明内容来自网络&#xff0c;将内容拼接整理出来的文档 Playwright 是微软开发的自动化测试工具&#xff0c;支持 Chrome、Firefox、Safari 等主流浏览器&#xff0c;提供多语言 API&#xff08;Python、JavaScript、Java、.NET&#xff09;。它的特点包括&a…...

vscode(仍待补充)

写于2025 6.9 主包将加入vscode这个更权威的圈子 vscode的基本使用 侧边栏 vscode还能连接ssh&#xff1f; debug时使用的launch文件 1.task.json {"tasks": [{"type": "cppbuild","label": "C/C: gcc.exe 生成活动文件"…...

深入理解JavaScript设计模式之单例模式

目录 什么是单例模式为什么需要单例模式常见应用场景包括 单例模式实现透明单例模式实现不透明单例模式用代理实现单例模式javaScript中的单例模式使用命名空间使用闭包封装私有变量 惰性单例通用的惰性单例 结语 什么是单例模式 单例模式&#xff08;Singleton Pattern&#…...

2.Vue编写一个app

1.src中重要的组成 1.1main.ts // 引入createApp用于创建应用 import { createApp } from "vue"; // 引用App根组件 import App from ./App.vue;createApp(App).mount(#app)1.2 App.vue 其中要写三种标签 <template> <!--html--> </template>…...

解决本地部署 SmolVLM2 大语言模型运行 flash-attn 报错

出现的问题 安装 flash-attn 会一直卡在 build 那一步或者运行报错 解决办法 是因为你安装的 flash-attn 版本没有对应上&#xff0c;所以报错&#xff0c;到 https://github.com/Dao-AILab/flash-attention/releases 下载对应版本&#xff0c;cu、torch、cp 的版本一定要对…...

Maven 概述、安装、配置、仓库、私服详解

目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...

鸿蒙DevEco Studio HarmonyOS 5跑酷小游戏实现指南

1. 项目概述 本跑酷小游戏基于鸿蒙HarmonyOS 5开发&#xff0c;使用DevEco Studio作为开发工具&#xff0c;采用Java语言实现&#xff0c;包含角色控制、障碍物生成和分数计算系统。 2. 项目结构 /src/main/java/com/example/runner/├── MainAbilitySlice.java // 主界…...