常见的死锁情况分析

embedded/2025/3/17 0:23:21/

 死锁

定义:

是指多个进程或线程在执行过程中,由于竞争资源或因通信的需要而产生的相互等待的状态,使得它们无法继续执行下去(单线程中使用不恰当也会导致死锁问题)。

如下为常见的死锁原因:

a. 互斥条件

至少有一个资源必须处于不可共享的状态,即某个资源在同一时刻只能被一个线程或进程占用。如果其他线程或进程请求该资源,它们必须等待该资源被释放。

b. 请求与保持条件

一个线程已经持有至少一个资源,但又请求其他线程占有的资源,并且在等待的过程中保持对已占有资源的控制。

c. 不剥夺条件

已经分配给一个线程的资源,在该线程使用完之前不能被强制剥夺。资源只能由线程自己释放。

d. 循环等待条件

在一个线程的等待链中,存在一个环路,使得每个线程都在等待另一个线程释放资源。即形成了一种“循环等待”状态。

如下为常见的死锁原因对应代码分析:

#include <iostream>
#include <mutex>
#include <thread>
using namespace std;std::mutex mtxAB;
std::recursive_mutex rmtxAB;
std::mutex mtx_C;
std::mutex mtx_D;void B(void);
void B1(void);// 死锁1——demo1
void A(void)
{lock_guard<std::mutex> lock(mtxAB);// 保护资源B();
}void B(void)
{A();
}// 死锁2——demo2
void A1(void)
{// lock_guard<std::mutex> lock(mtxAB);// 解决这种死锁方法lock_guard<std::recursive_mutex> lock(rmtxAB);// 保护资源B1();
}void B1(void)
{// lock_guard<std::mutex> lock(mtxAB);// 解决这种死锁方法lock_guard<std::recursive_mutex> lock(rmtxAB);    // 保护资源
}// 死锁3——demo1
void C(void)
{// 先获取mtx_Cstd::lock_guard<std::mutex> lock1(mtx_C);// 保护资源printf("C: operate something\n");// 再获取mtx_Dstd::lock_guard<std::mutex> lock2(mtx_D);
}void D(void)
{// 先获取mtx_Dstd::lock_guard<std::mutex> lock1(mtx_D);// 保护资源printf("D: operate something\n");// 再获取mtx_Cstd::lock_guard<std::mutex> lock2(mtx_C);
}int main()
{// test死锁1——demo1// A();// test死锁2——demo2A1();    // test死锁3——demo3// std::thread thread[] = {//     std::thread(C),//     std::thread(D)// };// for(auto& t : thread){//     t.join();// }// 死锁4...printf("the main normally exit!\n");return 0;
}

那么如何解决死锁呢? 

1. 避免嵌套锁(或锁的顺序)

最常见的避免死锁的方法之一是确保线程按固定的顺序获取锁。比如,规定所有线程首先获得 MutexA 锁,然后再获得 MutexB 锁,而不允许线程先获得 MutexB 锁再去获得 MutexA 锁。

(可自行尝试解决如上代码中demo3)

2. 使用超时机制

设置锁的超时时间。如果线程在获取锁时超时了,就主动释放已持有的锁,并重新尝试或返回失败,这样就可以避免死锁的发生。

3. 死锁检测与恢复

某些系统会周期性检查是否存在死锁。如果发现死锁状态,可以通过终止一个或多个线程、回滚某些操作或强制释放锁来恢复。

4. 使用递归锁

在一些情况下,如果锁定的资源是递归锁,同一线程可以多次获取锁,而不会导致死锁。这对于某些设计中需要递归调用的情况有效。

end!

各位大佬有什么补充,或者需要更正的欢迎指出哈~

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

相关文章

STM32U575RIT6单片机(一)

作业一&#xff1a;自己实现寄存器配置点亮LED1。 作业二&#xff1a;寄存器配置打开风扇&#xff0c;打开蜂鸣器。 //1、使能系统时钟 // 系统时钟初始化 - 不加入会报错 可以尝试一下 void SystemInit(void) {//对地址 0xE000ED88 的内容 进行修改://将0X3向左移动20位 或上…

2025-03-15 Python深度学习2——Numpy库

文章目录 1 基础1.1 数据类型1.1.1 整型数组与浮点型数组1.1.2 元素同化1.1.3 数组类型转换 1.2 数组维度1.2.1 一维数组与二维数组1.2.2 数组形状变换 2 创建数组2.1 创建指定数组2.2 创建递增数组2.3 创建同值数组2.4 创建随机数组 3 索引3.1 访问数组元素3.1.1 访问向量3.1.…

SOME/IP-SD -- 协议英文原文讲解8

前言 SOME/IP协议越来越多的用于汽车电子行业中&#xff0c;关于协议详细完全的中文资料却没有&#xff0c;所以我将结合工作经验并对照英文原版协议做一系列的文章。基本分三大块&#xff1a; 1. SOME/IP协议讲解 2. SOME/IP-SD协议讲解 3. python/C举例调试讲解 5.1.4.4 S…

《灵珠觉醒:从零到算法金仙的C++修炼》卷三·天劫试炼(40)翻天印压回文串 - 最长回文子序列(区间DP)

《灵珠觉醒:从零到算法金仙的C++修炼》卷三天劫试炼(40)翻天印压回文串 - 最长回文子序列(区间DP) 哪吒在数据修仙界中继续他的修炼之旅。这一次,他来到了一片神秘的回文森林,森林中有一本古老的翻天印,印身闪烁着神秘的光芒。森林的入口处有一块巨大的石碑,上面刻着…

作业9 (2023-05-05 数组的定义和初始化)

第1题/共11题【单选题】 关于一维数组初始化,下面哪个定义是错误的?( ) A.int arr[10] = {1,2,3,4,5,6}; B.int arr[] = {1,2,3,4,5,6}; C.int arr[] = (1,2,3,4,5,6); D.int arr[10] = {0}; A:正确,10个int的一段连续空间,前6个位置被初始化为1,2,3,4,5,6,其他…

《基于机器学习(xgboost)的人体卡路里消耗预测系统》开题报告

目录 1 选题的背景和意义 1.1 选题的背景 1.2 国内外研究现状及发展趋势 2 研究的基本内容 2.1 基本框架 2.1.1数据输入模块 2.1.2数据预处理模块 2.1.3特征工程模块 2.1.4模型训练与评估模块 2.1.5预测与输出模块 2.1.6用户界面(UI) 2.1.7系统维护与更新模块 2.…

MATLAB中envelope函数使用

目录 说明 示例 chirp 的解析包络 使用滤波器计算多通道信号的解析包络 录音信号的移动 RMS 包络 语音信号的峰值包络 不对称序列的包络 envelope函数的功能是提取信号的包络。 语法 [yupper,ylower] envelope(x) [yupper,ylower] envelope(x,fl,analytic) [yupper,…

STM32驱动代码规范化编写指南(嵌入式C语言方向)

点击下面图片&#xff0c;为您提供全新的嵌入式学习路线 文章目录 一、命名规范体系1.1 变量/函数命名1.2 宏定义规范1.3 类型定义 二、代码结构组织2.1 文件组织结构2.2 头文件规范模板 三、注释体系构建3.1 Doxygen风格示例3.2 复杂逻辑注释 四、硬件抽象层设计4.1 寄存器封…