python中的互斥锁、死锁、递归锁

互斥锁

Python中的互斥锁(Mutex,Mutual Exclusion)是一种同步原语,用于防止多个线程同时访问共享资源。在Python中,互斥锁通常是通过threading模块中的Lock类来实现的。

前面文章将的就是互斥锁:python中的锁

死锁

互斥锁的使用过程中, 如果使用不当,不管是进程或线程的Lock都会出现死锁。死锁(Deadlock)是多线程编程中常见的问题,是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象

比如当线程A持有资源1并等待获取资源2,而线程B持有资源2并等待获取资源1时,如果没有任何机制来解决这种循环等待,那么这两个线程就会陷入死锁状态,导致程序无法继续执行。此时称程序处于死锁状态或产生了死锁。

Python标准库中的threading模块并没有直接提供避免死锁的机制,因此开发者需要自己设计策略来预防或解决死锁问题。

必要条件

1. 互斥条件(Mutual Exclusion)

资源不能被多个进程同时使用,即每个资源要么是空闲的,要么是被一个进程独占使用。例如,打印机、数据库连接等都是互斥资源。

2. 占有且等待(Hold and Wait)

一个进程已经持有了至少一个资源,并且在等待获取额外的资源,而这些额外的资源被其他进程持有。例如,一个线程已经持有了 lock1,但在等待 lock2,而 lock2 被另一个线程持有。

3. 不可剥夺(No Preemption)

资源不能被强制从一个进程中剥夺,只能由持有该资源的进程显式地释放。例如,一个线程不能强制另一个线程释放它正在使用的锁。

4. 循环等待(Circular Wait)

存在一个进程链,使得每个进程都在等待链中的下一个进程所持有的资源。例如,线程 A 持有 lock1 并等待 lock2,而线程 B 持有 lock2 并等待 lock1

死锁的情况

多个锁

当多个线程尝试以不同的顺序获取多个锁时,可能会发生死锁。

import threadinglock1 = threading.Lock()
lock2 = threading.Lock()def thread1():with lock1:print("Thread 1 acquired lock 1")# 模拟一些工作with lock2:print("Thread 1 acquired lock 2")def thread2():with lock2:print("Thread 2 acquired lock 2")# 模拟一些工作with lock1:print("Thread 2 acquired lock 1")t1 = threading.Thread(target=thread1)
t2 = threading.Thread(target=thread2)t1.start()
t2.start()t1.join()
t2.join()print("程序结束")
  1. 线程 1 先获取 lock1,然后尝试获取 lock2
  2. 线程 2 先获取 lock2,然后尝试获取 lock1

如果线程 1 在持有 lock1 的同时,线程 2 持有 lock2,那么两个线程都会等待对方释放锁,从而导致死锁。

嵌套锁

import threading
from threading import Locklock = Lock()with lock:with lock:print(threading.current_thread())print("程序完成")
  1. 第一次获取锁with lock: 成功获取了 lock
  2. 第二次获取同一个锁:再次尝试 with lock:,但此时 lock 已经被当前线程持有,因此会阻塞在这里,导致死锁。

解决

方法一:破坏互斥条件

尽量减少对互斥资源的需求,例如通过设计无锁的数据结构或使用读写锁来允许并发访问。

方法二:破坏占有且等待条件

确保在请求新的资源之前释放当前持有的所有资源。可以通过一次性申请所有需要的资源来实现这一点。

方法三:破坏不可剥夺条件

允许抢占,即当某个进程请求某个已被分配给其他进程但尚未使用完毕的资源时,可以强制将该资源从当前持有者那里抢过来分配给请求者。

方法四:破坏循环等待条件

对所有可能申请到的资源进行排序,并要求所有进程按顺序申请。这种方法可以通过对所有锁进行排序并按顺序获取来实现。

多个锁解决

确保所有线程以相同的顺序获取多个锁。例如,可以确保所有线程总是先获取 lock1,然后再获取 lock2

import threadinglock1 = threading.Lock()
lock2 = threading.Lock()def thread1():with lock1:print("Thread 1 acquired lock 1")# 模拟一些工作with lock2:print("Thread 1 acquired lock 2")def thread2():with lock1:  # 改变这里,使得 thread2 与 thread1 一致地先获取 lock1print("Thread 2 acquired lock 1")# 模拟一些工作with lock2:print("Thread 2 acquired lock 2")t1 = threading.Thread(target=thread1)
t2 = threading.Thread(target=thread2)t1.start()
t2.start()t1.join()
t2.join()print("程序结束")

嵌套锁解决

import threading
from threading import RLocklock = RLock()with lock:with lock:print(threading.current_thread())print("程序完成")

使用递归锁:RLock,它允许同一个线程多次获取同一个锁,而不会导致死锁。

递归锁

递归锁(Reentrant Lock,简称 RLock)是一种特殊类型的锁,它允许同一个线程多次获取同一个锁,而不会导致死锁。递归锁内部维护了一个计数器,每次成功获取后计数器加一,释放时计数器减一,当计数器为零时才真正释放该资源。

递归锁的特性

  1. 可重入性:同一个线程可以多次获取同一个递归锁,而不会被阻塞。
  2. 计数器:递归锁内部维护了一个计数器,每次获取锁时计数器加一,释放时计数器减一。当计数器为零时,锁才真正被释放。
  3. 避免死锁:在需要多次获取同一个锁的情况下,可以避免死锁问题。

使用场景

递归锁通常用于需要在同一个线程中多次进入临界区的情况,例如递归函数调用或嵌套的 with 语句。

import threading
from threading import RLock, Locklock = RLock()
# lock = Lock()def recursive_function(n):with lock:print(f"当前函数第 {n} 层")if n > 0:recursive_function(n - 1)print(f"当前函数第 {n} 层")# 创建并启动线程
t = threading.Thread(target=recursive_function, args=(3,))
t.start()
t.join()print("程序完成")

在这个示例中,recursive_function 函数在每个级别都尝试获取同一个递归锁 lock

由于使用了 RLock,所以即使是同一个线程多次获取该锁也不会导致死锁。

但是如果使用的是普通互斥锁 Lock,当 recursive_function 尝试第二次获取 lock 时会阻塞自己,从而导致死锁。

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

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

相关文章

FreeRTOS学习笔记-基于stm32(7)任务状态查询与任务时间统计API函数

1、FreeRTOS任务相关API函数 函数描述uxTaskPriorityGet()查询某个任务的优先级vTaskPrioritySet()改变某个任务的任务优先级uxTaskGetSystemState()获取系统中任务状态vTaskGetInfo()获取某个任务信息xTaskGetApplicationTaskTag()获取某个任务的标签(Tag)值xTaskGetCurrentT…

idea 快捷键运用

ctrl d 向下复制一行 shiftalt↑/↓ 向上或者向下移动光标所在行 shiftctrl↑/↓ 向上或者向下移动光标所在行(自动对齐) shift F6 rename包名或者类名或者批量修改变量名(不建议更改项目名,包名也尽量别改) 输入if 然后ctrlshift回车 补全缺失的括号 shift …

C# 证件照替换底色与设置背景图---PaddleSegSharp

PaddleSegSharp 是一个基于百度飞桨PaddleSeg项目的人像分割模块而开发的.NET的工具类库。 PaddleSegSharp 中PaddleSeg.dll文件是基于开源项目最新发布版本PaddleSeg PaddleSeg的版本修改而成的C动态库,基于opencv的x64编译而成的。 PaddleSeg是基于飞桨PaddlePa…

在RT-Thread下为MPU手搓以太网MAC驱动-3

文章目录 MAC驱动支持不同的PHY芯片关于对PHY设备抽象的改进RT-Thread下PHY设备抽象接口的改进关于对PHY设备抽象的改进 这是个人驱动开发过程中做的一些记录,仅代表个人意见和理解,不喜勿喷 MAC驱动需要支持不同的PHY芯片 MAC驱动支持不同的PHY芯片 关…

spring分析工具_springboot startup analyze的部署和使用

工具是开源工具 ,可以放心使用 我是从开源中国OCSChina看到的顺便安利一下 部署 教程 https://github.com/linyimin0812/spring-startup-analyzer 直接下载地址 https://github.com/linyimin0812/spring-startup-analyzer/releases/download/v3.0.0/spring-startup-analyzer.…

Javaweb基础之Filter

大家好,这里是教授.F 引入: 为什么需要过滤器???我们在访问一个项目的时候,常常有很多页面,如果没有过滤器,则我们需要在用户访问一个页面的时候,都要进行一个校验&…

文心智能体平台:快来创建你的Java学习小助理,全方位辅助学习

文章目录 一、文心智能体平台1.1平台介绍1.2智能体介绍 二、智能体创建三、体验与总结 一、文心智能体平台 文心智能体平台是百度推出的基于文心大模型的智能体(Agent)平台,支持广大开发者根据自身行业领域、应用场景,选取不同类…

Python 元组

(1)元组中只包含一个元素时,需要在元素后面添加逗号: tup1 (50,); (2)元组中的元素值是不允许修改的,但我们可以对元组进行连接组合: tup1 (12, 34.56); tup2 (abc, xyz);# 以…

3D模型三角面转四角面操作指南---模大狮模型网

在3D建模的过程中,三角面(Triangles)和四角面(Quads)是两种常见的多边形类型。虽然三角面在渲染速度和计算效率上有其优势,但四角面在模型编辑和纹理映射上通常更为方便。因此,将三角面转换为四角面是建模过程中常见的需求。 一、选择合适的建…

【数值计算方法】雅可比解线性方程

废话少说&#xff0c;直接上干货。 #include <stdio.h> #include <stdlib.h> #include <math.h> #define MaxSize 100 double A[MaxSize][MaxSize]; //系数矩阵 double B[MaxSize]; //系数矩阵 double C[MaxSize][MaxSize]; //去对角线矩阵 double …

window自动启动bat文件

开机自动开启远程桌面&#xff0c; WinR 执行netplwiz 命令进入设置&#xff1b;取消勾选&#xff0c;可选择所需用户&#xff0c;点击应用&#xff0c;输入远程的密码即可 开机自动开启远程桌面&#xff0c; WinR 执行netplwiz 命令进入设置&#xff1b;取消勾选&#xff0…

python 面对对象 类 基础

面对对象 程序是由数据和功能组合而成的对象构建起来的&#xff0c;对数据与函数绑定到一起&#xff0c;进行封装&#xff0c;能够更快速的开发程序&#xff0c;减少重复代码 class --- 类&#xff0c;类是对象的抽象化&#xff0c;具有相同特征或行为的事物的统称 类的定义…

JavaScript className 类名属性操作

在JavaScript中&#xff0c;可以通过className属性来操作HTML元素的类名。 添加类名&#xff1a;可以使用element.className "className"来添加一个类名到元素中。 var element document.getElementById("myElement"); element.className " newC…

web题解 Easy_SQLi or 雏形系统 (解题方法思想)

1.Easy_SQLi 1&#xff09;打开题目环境&#xff0c;如下是一个类似弱密码的格式&#xff0c;但是它又说是sql&#xff0c;还是按sql注入来 2&#xff09;.这里我尝试判断它的注入类型&#xff0c;但是一只不对&#xff0c;我便想着用万能密码试试&#xff0c;怎料直接登录成功…

STL库--priority_queue

目录 priority_queue定义 prority_queue容器内元素的访问 priority_queue()常用函数实例解析 priority_queue内元素优先级的设置 priority_queue的常见用途 priority_queue又称为优先队列&#xff0c;其底层是用堆来进行实现的。在优先队列中&#xff0c;队首元素一定是当…

前端基础入门三大核心之JS篇:BOM基础探索 —— 窥视window对象的奥秘【含代码示例】

前端基础入门三大核心之JS篇&#xff1a;BOM基础探索 —— 窥视window对象的奥秘【含代码示例】 一、window对象&#xff1a;你的浏览器之窗基本概念作用说明 二、代码示例&#xff1a;玩转window案例一&#xff1a;简单弹窗与控制台输出案例二&#xff1a;窗口尺寸调整事件案例…

2024广东省赛 I.不等式

题目 #include <bits/stdc.h> using namespace std; #define int long long#define pb push_back#define fi first#define se second#define lson p << 1#define rson p << 1 | 1#define ll long longconst int maxn 1e6 5, inf 1e9, maxm 4e4 5, base…

全文最详细的生产管理完整方案!那些让人头疼的生产管理难题及解决方法!

什么是生产管理系统&#xff1f;为何生产管理系统在企业管理中如此重要&#xff1f;生产管理系统的核心模块包括哪些&#xff1f;为何企业在生产管理系统中常常遭遇项目信息碎片化、任务分配和跟踪困难等痛点&#xff1f;又该如何针对生产管理痛点进行优化&#xff1f; 本文40…

汽车大灯中擎耀智能控制器在车灯智能化配置下的创新与分析

随着科技的飞速发展&#xff0c;汽车工业也在不断地进行着革新。其中&#xff0c;车灯作为汽车的重要组成部分&#xff0c;其智能化配置已经成为汽车行业的一大趋势。这种趋势不仅为消费者带来了更加安全、便捷的驾驶体验&#xff0c;同时也为商家提供了丰富的商业机会。汽车工…

「异步魔法:Python数据库交互的革命」(二)

哈喽&#xff0c;我是阿佑&#xff0c;上篇文章带领了大家跨入的异步魔法的大门——Python数据库交互&#xff0c;一场魔法与技术的奇幻之旅&#xff01; 从基础概念到DB-API&#xff0c;再到ORM的高级魔法&#xff0c;我们一步步揭开了数据库操作的神秘面纱。SQLAlchemy和Djan…