【C++并发编程】(六)死锁问题

(六)解决死锁问题

死锁(Deadlock)是指两个或两个以上的进程(或线程)在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程(或线程)称为死锁进程(或线程)。

死锁的发生通常需要同时满足四个必要条件:

  1. 互斥条件(Mutual Exclusion):某个资源在一段时间内只能由一个进程占有,不能同时被两个或两个以上的进程(或线程)占有。这意味着,当一个进程(或线程)正在使用一个资源时,其他进程(或线程)必须等待该进程释放资源后才能使用。
  2. 占有且等待条件(Hold and Wait):进程(或线程)至少已经占有一个资源,但又申请新的资源。这意味着,进程(或线程)在等待其他资源释放时,不会释放已经占有的资源。
  3. 不可抢占条件(No Preemption):一个进程(或线程)所占有的资源在其使用完之前,不能被其他进程(或线程)强行夺走,只能由该进程(或线程)用完之后主动释放。这个条件保证了资源的稳定使用,但也可能导致死锁。
  4. 循环等待条件(Circular Wait):存在一个进程(或线程)等待序列{P1, P2, …, Pn},其中P1等待P2所占有的某个资源,P2等待P3所占有的某个资源,…,而Pn等待P1所占有的某个资源,从而形成一个进程(或线程)循环等待的局面。

下面是一个有死锁问题的示例:

#include <iostream>  
#include <thread>  
#include <mutex>  
  
std::mutex mtxA;  
std::mutex mtxB;  
  
void threadFunction1() {  
    // 线程1先锁定互斥锁A  
    std::lock_guard<std::mutex> lockA(mtxA);  
    std::cout << "Thread 1: Locked mtxA, trying to lock mtxB\n";  
  
    // 试图锁定互斥锁B,若此时如果线程2已经锁定了mtxB,则线程1会阻塞  
    std::lock_guard<std::mutex> lockB(mtxB); 
    std::cout << "Thread 1: Locked mtxB\n";  

}  
  
void threadFunction2() {  
    // 线程2先锁定互斥锁B  
    std::lock_guard<std::mutex> lockB(mtxB);  
    std::cout << "Thread 2: Locked mtxB, trying to lock mtxA\n";  
  
    // 试图锁定互斥锁A,但此时如果线程1已经锁定了mtxA,则线程2会阻塞  
    std::lock_guard<std::mutex> lockA(mtxA); 
    std::cout << "Thread 2: Locked mtxA\n";  

}  
  
int main() {  
    // 创建两个线程  
    std::thread t1(threadFunction1);  
    std::thread t2(threadFunction2);  
  
    // 等待两个线程完成  
    t1.join();  
    t2.join();  
  
    return 0;  
}
Thread 1: Locked mtxA, trying to lock mtxB
Thread 2: Locked mtxB, trying to lock mtxA

在这个示例中:

  • 互斥条件:由std::mutex类保证,每个互斥锁在同一时间只能被一个线程锁定。
  • 占有且等待条件:线程1锁定了mtxA并等待mtxB,而线程2锁定了mtxB并等待mtxA
  • 不可抢占条件:一旦线程锁定了互斥锁,没有其他线程可以抢占它,直到原始线程调用解锁。
  • 循环等待条件:线程1等待线程2释放mtxB,而线程2等待线程1释放mtxA,形成了一个循环等待。

由于这四个条件都满足,因此这个程序可能会导致死锁。

为了解决上述代码中的死锁问题,我们可以使用一种称为“锁顺序”或“锁排序”的策略,即确保所有线程都以相同的顺序请求锁。这样可以避免循环等待的情况,从而防止死锁:

#include <iostream>
#include <thread>
#include <mutex>
  
std::mutex mtxA;  
std::mutex mtxB;  
  
void threadFunction1() { 
    std::unique_lock<std::mutex> lockA(mtxA, std::defer_lock); // 延迟锁定 mtxA
    std::unique_lock<std::mutex> lockB(mtxB, std::defer_lock); // 延迟锁定 mtxB

    // 使用 std::lock 确保两个锁以正确的顺序被锁定
    std::lock(lockA, lockB);
    // mtxA 和 mtxB 都被锁定了,并且是按照正确的顺序
    std::cout << "Thread 1: Both mtxA and mtxB are locked.\n";

}  
  
void threadFunction2() {  
    std::unique_lock<std::mutex> lockB(mtxB, std::defer_lock); // 延迟锁定 mtxB
    std::unique_lock<std::mutex> lockA(mtxA, std::defer_lock); // 延迟锁定 mtxA
    
    // 使用 std::lock 确保两个锁以正确的顺序被锁定
    std::lock(lockA, lockB);
    // mtxA 和 mtxB 都被锁定了,并且是按照正确的顺序
    std::cout << "Thread 2: Both mtxA and mtxB are locked.\n";

}  
  
int main() {  
    // 创建两个线程  
    std::thread t1(threadFunction1);  
    std::thread t2(threadFunction2);  
  
    // 等待两个线程完成  
    t1.join();  
    t2.join();  
  
    return 0;  
}
Thread 1: Both mtxA and mtxB are locked.
Thread 2: Both mtxA and mtxB are locked.

在上面的代码中使用 std::unique_lockstd::lock 来确保 mtxAmtxB 以正确的顺序被锁定。std::lock 函数会尝试以原子方式锁定多个互斥锁,如果无法立即获得所有锁,它会阻塞直到所有锁都变得可用,或者抛出异常(取决于 std::lock 的配置)。

注意,std::unique_lock 的构造函数中使用了 std::defer_lock 参数来延迟锁定(std::lock_guard 不支持延迟锁定),这样我们才可以在调用 std::lock 时以原子方式锁定多个互斥锁。如果直接使用 std::lock_guard 而不延迟锁定,那么每个 std::lock_guard 会立即尝试锁定其对应的互斥锁,这样就无法使用 std::lock 来协调多个锁的锁定顺序了。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【LAMMPS学习】八、基础知识(5.9)LAMMPS 近场动力学

8. 基础知识 此部分描述了如何使用 LAMMPS 为用户和开发人员执行各种任务。术语表页面还列出了 MD 术语,以及相应 LAMMPS 手册页的链接。 LAMMPS 源代码分发的 examples 目录中包含的示例输入脚本以及示例脚本页面上突出显示的示例输入脚本还展示了如何设置和运行各种模拟。 …

渲染农场怎么渲染照片级效果图?

当讨论3D渲染的真实性时&#xff0c;不可避免地会将目光投向渲染农场。这些基于云的计算大军&#xff0c;专门负责逐帧打造接近现实的画面效果&#xff0c;无论是在电影动画还是在效果图制作等行业&#xff0c;都扮演着重要的支撑角色。对观众来说&#xff0c;画面的真实性几乎…

面试中算法(删去n个数字后的最小值)

有一个整数&#xff0c;从该整数中去掉n个数字&#xff0c;要求剩下的数字形成的新整数尽可能小。 分析&#xff1a;使用栈的特性&#xff0c;在遍历原整数的数字时&#xff0c;让所有数字一个一个入栈&#xff0c;当某个数字需要被删除时&#xff0c;&#xff08;即栈顶数字&g…

开源模型应用落地-CodeQwen模型小试-探索更多使用场景(三)

一、前言 代码专家模型是基于人工智能的先进技术&#xff0c;它能够自动分析和理解大量的代码库&#xff0c;并从中学习常见的编码模式和最佳实践。这种模型可以提供准确而高效的代码建议&#xff0c;帮助开发人员在编写代码时避免常见的错误和陷阱。 通过学习代码专家模型&…

高效项目管理:如何利用zz-plan在线甘特图工具

作为项目管理人员&#xff0c;使用 zz-plan https://zz-plan.com/这样的在线甘特图协作软件可以极大地提高项目管理的效率和效果。以下是结合zz-plan特点的一些关键步骤&#xff1a; 1. 制定项目计划 在zz-plan上创建新的项目&#xff0c;定义项目目标、关键里程碑和最终期限。…

大学数据结构学不进去怎么办?

在开始前我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「数据结构的资料从专业入门到高级教程」&#xff0c; 点个关注在评论区回复“888”之后私信回复“888”&#xff0c;全部无偿共享给大家&#xff01;&#xff01;&#xff01;除了极少数的“算法天才”&a…

Google Play开发者账号为什么会被封?如何解决关联账号问题?

Google Play是Google提供的一个应用商店&#xff0c;用户可以在其中下载并安装Android设备上的应用程序、电影、音乐、电子图书等。Google Play是Android平台上较大的应用市场&#xff0c;包含了数百万个应用程序和游戏。但是谷歌对于上架应用的审核越趋严格&#xff0c;开发者…

全新Adobe利器:Project Neo为2D平面图像轻松添加3D立体效果

Adobe的崭新创意工具Project Neo&#xff0c;正以其独特的3D技术为传统的2D图像设计领域带来革命性的变化。这款工具的核心功能在于&#xff0c;它能够将原本平面的2D图像巧妙地转化为立体感十足的三维作品。 想象一下&#xff0c;你手中的图标、动画插图&#xff0c;在Projec…

XSS漏洞---XSS-labs通关教程

文章目录 前言一、pandas是什么&#xff1f;二、使用步骤 1.引入库2.读入数据总结 Level-1 过滤源码&#xff1a;无 pyload&#xff1a; name<script>alert(1)</script> Level-2 过滤源码&#xff1a;利用转译函数将特殊字符转译为实体字符 $str $_GET["…

软件系统概要设计说明书(实际项目案例整理模板套用)

系统概要设计说明书 1.整体架构 2.功能架构 3.技术架构 4.运行环境设计 5.设计目标 6.接口设计 7.性能设计 8.运行设计 9.出错设计 全文档获取进主页 软件资料清单列表部分文档&#xff08;全套可获取&#xff09;&#xff1a; 工作安排任务书&#xff0c;可行性分析报告&…

Spring Data JPA的一对一、LazyInitializationException异常、一对多、多对多操作

Spring Data JPA系列 1、SpringBoot集成JPA及基本使用 2、Spring Data JPA Criteria查询、部分字段查询 3、Spring Data JPA数据批量插入、批量更新真的用对了吗 4、Spring Data JPA的一对一、LazyInitializationException异常、一对多、多对多操作 前言 通过前三篇Sprin…

ISIS的基本配置

1.IS-IS协议的基本配置&#xff08;1&#xff09; 2.IS-IS协议的基本配置&#xff08;2&#xff09; 3.IS-IS协议的基本配置&#xff08;3&#xff09; 4.案例&#xff1a;IS-IS配置 R1的配置如下&#xff1a; [AR1czy]isis 1 [AR1czy-isis-1]is-level level-1 [AR1czy-isis-…

设置 kafka offset 消费者位移

文章目录 1.重设kafka消费者位移2.示例2.1 通过 offset 位置2.2 通过时间2.3 设置到最早 1.重设kafka消费者位移 维度策略含义位移Earliest把位移调整到当前最早位移处位移Latest把位移调整到当前最新位移处位移Current把位移调整到当前最新提交位移处位移Specified-Offset把位…

爬虫学习(4)每日一笑

代码 import requests import re import osif __name__ "__main__":if not os.path.exists("./haha"):os.makedirs(./haha)url https://mlol.qt.qq.com/go/mlol_news/varcache_article?docid6321992422382570537&gameid3&zoneplat&webview…

算法分析 KMP算法中next值的计算、0/1背包问题

5.6.1 KMP算法中next值的计算 设模式的长度为m。用蛮力法求解 KMP算法中的 next值时&#xff0c;next[0]可直接给出&#xff0c;计算next[j](1<j<m-1)则需要在 T[0] …T[j-1]中分别取长度为j-1、..、2、1的真前缀和真后缀并比较是否相等&#xff0c;最坏情况下的时间代价…

数据库事务隔离级别及mysql实现方案

1、数据库的并发问题 以下几个概念是事务隔离级别要实际解决的问题&#xff0c;所以需要搞清楚都是什么意思。 脏读&#xff1a;读到了其他事务未提交的数据&#xff0c; 不可重复读&#xff1a;在一个事务内&#xff0c;多次读取的同一批数据出现不一致的情况。 幻读&…

CTK库编译-01

地址 官网地址&#xff1a;Commontk github地址&#xff1a;https://github.com/commontk/CTK 编译环境 Qt套件&#xff1a; IDE&#xff1a;VS2022 使用vs2022 文件->打开->cmake 修改根目录下的CMakeLists.txt 默认只编译core模块&#xff0c;所以需要把部分模块…

无偏扭曲区域采样在可微分渲染中的应用

图1. 可微渲染计算光传输方程的导数。为了处理可见性的存在&#xff0c;最近的基于物理的可微渲染器需要显式地找到边界点[Li等人2018; Zhang等人2020]&#xff0c;或者通过启发式方法近似边界贡献[Loubet等人2019]。我们从第一原理出发&#xff0c;开发了一个无偏估计器&#…

阿里巴巴alibaba国际站API接口:商品详情和关键词搜索商品列表

阿里巴巴国际站&#xff08;Alibaba.com&#xff09;提供了API接口供开发者使用&#xff0c;以实现与平台的数据交互。然而&#xff0c;由于API的详细内容和调用方式可能会随着时间和平台更新而发生变化&#xff0c;以下是一个概述和一般性的指导&#xff0c;关于如何使用阿里巴…

企业邮箱是什么?怎么注册一个企业邮箱?

企业邮箱是什么&#xff1f;有什么特征&#xff1f;企业邮箱的特征就是以企业域名为后缀。企业通过企业邮箱能够提升自身的品牌形象&#xff0c;还能够提高员工的工作效率。作为企业的管理者来说&#xff0c;应该如何注册一个企业邮箱呢&#xff1f;小编今天就为您介绍下企业邮…