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

常见的死锁情况分析

 死锁

定义:

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

如下为常见的死锁原因:

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_C
    std::lock_guard<std::mutex> lock1(mtx_C);
    // 保护资源
    printf("C: operate something\n");
    // 再获取mtx_D
    std::lock_guard<std::mutex> lock2(mtx_D);
}

void D(void)
{
    // 先获取mtx_D
    std::lock_guard<std::mutex> lock1(mtx_D);
    // 保护资源
    printf("D: operate something\n");
    // 再获取mtx_C
    std::lock_guard<std::mutex> lock2(mtx_C);
}

int main()
{
    // test死锁1——demo1
    // A();
    // test死锁2——demo2
    A1();    
    // 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!

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

相关文章:

  • 前端小食堂 | Day16 - 前端监控の天眼通
  • 【leetcode100】全排列Ⅱ
  • 不像人做的题————十四届蓝桥杯省赛真题解析(上)A,B,C,D题解析
  • VSCode通过SSH远程登录Windows服务器
  • 《SQL编程思想》中的 MySQL 建表语句和测试数据
  • C++友元
  • 如何让ai问答机器人通人性?
  • 在离线情况下如何使用 Python 翻译文本
  • Windows-PyQt5安装+PyCharm配置QtDesigner + QtUIC
  • lanqiaoOJ 1180:斐波那契数列 ← 矩阵快速幂
  • 【接口封装】——22、读写文件
  • Vuex 核心功能与组件通信
  • ThreadLocal(线程本地存储)
  • C++进阶——map和set的使用
  • SpringBoot项目部署到宝塔面板的详细过程
  • 关于解决新版本spring项目请求测试接口返回406的问题
  • 当AI学会“察言观色“:多模态情绪识别的魔幻现实主义之旅
  • BGP路由属性和选路
  • 左叶子之和 找左下角的值 路径总和
  • Ollama+OpenWebUI本地部署大模型
  • 苹果第二财季营收增长5%,受关税政策影响预计下一财季新增9亿美元成本
  • 以色列消防部门:已控制住耶路撒冷山火
  • 游客曝九寨沟打网约车被出租车围堵,景区回应:当地无合规网约车
  • 宋徽宗《芙蓉锦鸡图》亮相,故宫首展历代动物绘画
  • 铁路五一假期运输今日启动,预计发送旅客1.44亿人次
  • 四川省社科联期刊:不建议在读硕士、博士将导师挂名为第一作者