0109 图解多线程死锁问题

news2024/9/30 23:13:38

死锁场景

🤪举例场景:两个憨憨Tom和Sam去西餐厅吃牛排,桌子上只有一把刀和一把叉,Tom先拿到了叉子,Sam拿到了刀。只有同时拿到刀叉才能吃牛排,于是两个憨憨陷入如下的僵局。

死锁案例

这个场景中,就存在死锁形成的条件:

✍️ 互斥条件:一张桌子上只有一把餐刀和一把叉子,且只能由一个人同时使用。Tom和Sam需要同时拿到餐刀和叉子才能吃饭,他们不能共享餐具。
餐刀和叉子是资源,它们只能由一个人独占使用,另一个人必须等待餐具的释放。

✍️ 请求与保持条件 :Sam已经拿到了餐刀,但还在等待拿到叉子;同时,他不会放下已经拿到的餐刀。Tom已经拿到了叉子,但在等待餐刀,也不会先放下叉子。

Sam拥有一部分资源(餐刀),并且在等待另一部分资源(叉子),而Tom则持有叉子,等待餐刀。这导致两个人都无法继续用餐

✍️ 不剥夺条件:一旦一个人拿到了餐刀或叉子,另一个人不能强行夺走它,必须等第一个人自愿放下餐具。

✍️ 循环等待条件:Sam人拿着餐刀,等待叉子,而Tom拿着叉子,等待餐刀。这样就形成了一个循环:Tom等待Sam释放资源,Sam等待Tom释放资源,两者都陷入了无限等待的状态。

死锁形成的原因

形成死锁有四个必要条件,分别是:

  • 互斥条件(Mutual Exclusion):资源是独占的,即某个资源一次只能被一个线程持有。如果有其他线程请求资源,就必须等待。
  • 请求与保持条件(Hold and Wait):一个线程已经持有了一个资源,并且还在等待获取其他线程持有的资源,而不释放它已占有的资源。
  • 不剥夺条件(No Preemption):已经获得的资源在未使用完之前不能被强制剥夺,只能由持有该资源的线程主动释放。
  • 循环等待条件(Circular Wait):存在一个线程等待的循环链,每个线程都在等待下一个线程所持有的资源,从而形成了一个闭环。

如何预防死锁

接着看怎么来解决这两个憨憨的吃饭问题。

破坏互斥条件

在这里插入图片描述

第一种避免死锁的方法就是破坏互斥条件:通过增加资源的数量(例如多备几套餐具),可以避免资源独占,从而打破互斥条件。现实中,可以通过增加并行资源或共享资源的方式来减少死锁的可能性。

破坏请求与保持条件

在这里插入图片描述

第二种避免死锁的方法就是破坏请求与保持条件:规定每个人必须在没有拿任何餐具的时候,先一次性拿好所需的餐刀和叉子。如果他发现没有足够的餐具可用(例如只剩餐刀或叉子),他必须先放下自己手中的餐具,等待下一轮再尝试拿齐。

这种方法避免了“请求与保持”,要求在拥有资源之前确保拿到所有需要的资源。如果无法一次性拿到,必须释放已经持有的资源,这样不会出现资源的相互依赖。

破坏不剥夺条件

在这里插入图片描述

第三种预防死锁的方式就是破坏不剥夺条件:设定一个规则,如果某个人在拿到餐刀后长时间没有拿到刀,就必须自动放下餐具,以便其他人使用。

这种方式打破了不剥夺条件,使资源能够被强制释放。对于计算机系统,可以通过设置超时机制或者资源预占的方式来避免死锁。例如,当一个线程长时间占有资源而没有完成任务时,可以强制回收资源,让其他线程使用。

破坏循环等待条件

在这里插入图片描述

第四种预防死锁的方式就是破坏循环等待条件:规定用餐时必须按照固定顺序拿餐具,例如每个人必须先拿叉,再拿刀。这样,就不会出现两个人一个拿刀一个拿叉、互相等待的情况。

通过规定资源获取的顺序,可以避免形成环形依赖链。在计算机系统中,也可以通过全局的资源顺序策略,确保线程在请求多个资源时,必须按照固定的顺序请求,从而避免循环等待。

如何避免死锁

银行家算法是一种资源分配和死锁避免算法,主要用于确保在多个进程并发运行时系统不会进入不安全状态。以下是其原理和实现的详细介绍。

原理

  1. 安全状态与不安全状态
    • 系统在资源分配时要确保处于安全状态。如果有一种资源分配方式,能够保证所有进程最终都能完成,那么系统就是安全的。
    • 如果不能找到这样的分配方式,系统则处于不安全状态。
  2. 请求与分配
    • 每个进程在运行时会声明其最大资源需求(最大资源向量)。
    • 进程在执行期间会请求资源。算法会检查这个请求是否会导致系统进入不安全状态。
  3. 资源分配步骤
    • 当一个进程请求资源时,算法会进行以下检查:
      1. 检查请求是否小于或等于最大需求。
      2. 检查请求是否小于或等于当前可用资源。
      3. 假设资源被分配给该进程,检查系统是否依然安全。
    • 如果所有检查都通过,资源将被分配;否则,进程需要等待。

实现

以下是一个简单的银行家算法的实现思路:

  1. 数据结构

    • max[][]:每个进程的最大需求。
    • allocation[][]:当前已分配给每个进程的资源。
    • need[][]:每个进程还需要的资源,need[i][j] = max[i][j] - allocation[i][j]
    • available[]:当前可用的资源。
  2. 算法步骤

    public boolean requestResources(int processID, int[] request) {
        // 1. 检查请求是否有效
        for (int i = 0; i < resources.length; i++) {
            if (request[i] > need[processID][i] || request[i] > available[i]) {
                return false; // 请求无效
            }
        }
    
        // 2. 假设分配资源
        for (int i = 0; i < resources.length; i++) {
            available[i] -= request[i];
            allocation[processID][i] += request[i];
            need[processID][i] -= request[i];
        }
    
        // 3. 检查系统安全性
        if (isSafe()) {
            return true; // 资源分配成功
        } else {
            // 4. 回滚
            for (int i = 0; i < resources.length; i++) {
                available[i] += request[i];
                allocation[processID][i] -= request[i];
                need[processID][i] += request[i];
            }
            return false; // 资源分配失败
        }
    }
    
    private boolean isSafe() {
        boolean[] finish = new boolean[numProcesses];
        int[] work = available.clone();
        int count = 0;
    
        while (count < numProcesses) {
            boolean found = false;
            for (int i = 0; i < numProcesses; i++) {
                if (!finish[i] && canProceed(i, work)) {
                    for (int j = 0; j < resources.length; j++) {
                        work[j] += allocation[i][j];
                    }
                    finish[i] = true;
                    found = true;
                    count++;
                }
            }
            if (!found) break; // 无法找到可以执行的进程
        }
    
        // 如果所有进程都完成,则安全
        return count == numProcesses;
    }
    
    private boolean canProceed(int processID, int[] work) {
        for (int i = 0; i < resources.length; i++) {
            if (need[processID][i] > work[i]) {
                return false;
            }
        }
        return true;
    }
    

🤪欢迎关注作者,一起快乐的探索编程
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

信号处理快速傅里叶变换(FFT)的学习

FFT是离散傅立叶变换的快速算法&#xff0c;可以将一个信号变换到频域。有些信号在时域上是很难看出什么特征的&#xff0c;但是如果变换到频域之后&#xff0c;就很容易看出特征了。这就是很多信号分析采用FFT变换的原因。另外&#xff0c;FFT可以将一个信号的频谱提取出来&am…

可以白嫖PPT模板的6个网站,赶紧收藏

推荐6个PPT模板网站&#xff0c;免费下载&#xff0c;绝对的高质量&#xff0c;赶紧收藏&#xff01; 1、菜鸟图库 ppt模板免费下载|ppt背景图片 - 菜鸟图库 菜鸟图库网有非常丰富的免费素材&#xff0c;像设计类、办公类、自媒体类等素材都很丰富。PPT模板种类很多&#xff0…

高中教辅汇总【35GB】

文章目录 一、资源概览二、资源亮点三、获取方式 一、资源概览 这份教辅资源汇总&#xff0c;精心搜集了高中各学科的海量教辅资料&#xff0c;总容量高达35GB&#xff0c;覆盖了语文、数学、英语、物理、化学、生物、历史、地理、政治等所有必修及选修科目。从基础知识点到难…

windos gcc 安装

一、github&#xff1a;https://github.com/skeeto/w64devkit/releases 二、安装 三、打开安装目录 四、当前目录打开命令行&#xff0c;查看版本。如果有说明安装成功 五、将bin路径放入系统环境变量path里

Moki再次拉低睡前视频难度?还能轻松点赞过万!我只能说任重道远(附保姆级制作教程)

大家好&#xff0c;我是凡人。 是一个不黑、不吹、不跟风、有知识、有骨气的五好小号主。 就在昨天&#xff0c;收到了一条短信&#xff0c;内容是&#xff1a;美图Moki电脑端正式上线。 这时我才想到&#xff0c;很早之前就关注了这块自动生成AI短片的工具&#xff0c;宣传是…

CNN中注意力机制综合指南:从理论到Pytorch代码实现

注意力机制已经成为深度学习模型&#xff0c;尤其是卷积神经网络&#xff08;CNN&#xff09;中不可或缺的组成部分。通过使模型能够选择性地关注输入数据中最相关的部分&#xff0c;注意力机制显著提升了CNN在图像分类、目标检测和语义分割等复杂任务中的性能。本文将全面介绍…

redis的数据结构,内存处理,缓存问题

redisObject redis任意数据的key和value都会被封装为一个RedisObject&#xff0c;也叫redis对象&#xff1a; 这就redis的头信息&#xff0c;占有16个字节 redis中有两个热门数据结构 1.SkipList&#xff0c;跳表&#xff0c;首先是链表&#xff0c;和普通链表有以下差异&am…

【c++面试总结】

1. NULL 和 nullptr 区别 int overLoadTest(int x) {cout << __LINE__ << endl;return 0; }int overLoadTest(char* x) {cout << __LINE__ << endl;return 0; }int main() {char x[10] {1,2,3,4,5};overLoadTest(1);overLoadTest(x);overLoadTest(nu…

2024大二上js高级+ES6学习9.23(严格模式,this指向和改变this指向,高阶函数)

9.23.2024 函数进阶 1.函数定义方式 2.函数的调用方式 3.函数的this指向 而普通函数、定时器函数。立即执行函数一般是window调用的 构造函数调用&#xff1a;原型对象中的方法是在实例对象调用这个方法时&#xff0c;才指向实例对象。 4.改变函数的this指向&#xff08;ca…

体育馆智能化系统规划方案

1. 体育馆智能化系统规划概述 本文详细介绍了一个室内体育馆的智能化系统规划方案&#xff0c;旨在通过优化建筑结构、系统、服务和管理&#xff0c;创建一个高效、舒适、便利的环境。该体育馆按照国家三级乙等标准设计&#xff0c;总建筑面积约1.69万平方米&#xff0c;智能化…

基于深度学习的点云处理模型PointNet++学习记录

前面我们已经学习了Open3D&#xff0c;并掌握了其相关应用&#xff0c;但我们也发现对于一些点云分割任务&#xff0c;我们采用聚类等方法的效果似乎并不理想&#xff0c;这时&#xff0c;我们可以想到在深度学习领域是否有相关的算法呢&#xff0c;今天&#xff0c;我们便来学…

在树莓派上部署开源监控系统 ZoneMinder

原文&#xff1a;https://blog.iyatt.com/?p17425 前言 自己搭建&#xff0c;可以用手里已有的设备&#xff0c;不需要额外买。这套系统的源码是公开的&#xff0c;录像数据也掌握在自己手里&#xff0c;不经过不可控的三方。 支持设置访问账号 可以保存录像&#xff0c;启…

ST-GCN模型实现花样滑冰动作分类

加入深度实战社区:www.zzgcz.com&#xff0c;免费学习所有深度学习实战项目。 1. 项目简介 本项目实现了A042-ST-GCN模型&#xff0c;用于对花样滑冰动作进行分类。花样滑冰作为一项融合了舞蹈与竞技的运动&#xff0c;其复杂的动作结构和多变的运动轨迹使得动作识别成为一个具…

redis-数据类型

十大数据类型 学习 redis 操作手册 英文 Commands 中文 Redis命令中心&#xff08;Redis commands&#xff09; – Redis中国用户组&#xff08;CRUG&#xff09; 学习方法 举出一个数据结构的应用场景&#xff08;理解数据结构特点&#xff09;&#xff0c;并操作&…

深度学习模型可视化工具 Netron 使用教程

Netron 介绍 Netron 是一个用于可视化机器学习模型、深度学习模型、神经网络、图模型&#xff08;例如用于计算机视觉的 ONNX、Caffe、TensorFlow Lite、TensorFlow.js、Keras、Darknet、TVM、PyTorch、TorchScript、Core ML、ML.NET、NNEF、PaddlePaddle、OpenVINO、Arm NN等…

C++STL--------string

文章目录 一、STL介绍二、string1、constructor构造函数2、operator[]方括号运算符重载3、iterator迭代器4、reverse_iterator反向迭代器5、size和length6、capacity7、clear8、shrink_to_fit9、at10、push_back11、append 二、auto类型(C11)1、使用2、真正的价值 三、范围for(…

基于大数据技术的宠物商品信息比价及推荐系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码、微信小程序源码 精品专栏&#xff1a;…

自己做个国庆75周年头像生成器

版权声明&#xff1a;本文为博主原创文章&#xff0c;转载请在显著位置标明本文出处以及作者网名&#xff0c;未经作者允许不得用于商业目的。 下载相关代码&#xff1a;【免费】《自己做个国庆75周年头像生成器》代码资源-CSDN文库 又是一年国庆节&#xff0c;今年使用国旗做…

MFU简介

1、缩写 MFU - Mask Field Utilization&#xff08;光刻掩膜版有效利用比例&#xff09; GDPW - Gross Die Per Wafer&#xff0c;每张wafer上die的数量 2、什么是MASK 在光刻机中&#xff0c;光源&#xff08;紫外光、极紫外光&#xff09;透过mask曝光在晶圆上形成图…

华大HC32F448的FreeRTOS移植

为什么要移植FreeRTOS? 目前的程序只是前后台查询方式的架构&#xff0c;有些场合更适用FreeRTOS(免费使用)。 下载地址&#xff1a; 下载 FreeRTOS - FreeRTOS™ 相关知识入门&#xff1a; FreeRTOS™ - FreeRTOS™ &#xff08;网址&#xff09; FreeRTOSv9.0.0文件夹…