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

python 互斥量详解

python 互斥量详解

  • 一、python 互斥量详解
    • 1、什么是互斥量?
    • 2、Python 中的互斥量实现
      • 2.1 、`threading.Lock` (普通锁/互斥锁)
      • 2.2 、`threading.RLock` (可重入锁/递归锁)
    • 3、使用 `with` 语句管理锁
    • 4、互斥量与条件变量 (`threading.Condition`)
    • 5、注意事项与潜在问题
    • 6、其他同步工具 (简要提及)
    • 7、总结
  • 二、代码示例
    • 1、源码分享
    • 2、运行结果

一、python 互斥量详解

在多线程编程中,当多个线程需要访问共享资源(如变量、数据结构、文件等)时,如果不进行协调,可能会发生竞态条件(Race Condition),导致程序行为不确定或数据损坏。互斥量(Mutex, Mutual Exclusion)是解决这类问题的核心同步原语之一。

1、什么是互斥量?

互斥量是一种锁机制,用于确保在任何时刻,只有一个线程可以访问特定的共享资源或执行特定的代码段(称为临界区, Critical Section)。

  • 核心思想:当一个线程需要进入临界区时,它必须先获取(acquire)与该临界区关联的互斥锁。如果锁已被其他线程持有(获取),则当前线程会被阻塞(暂停执行),直到锁被释放。
  • 操作
    • acquire():尝试获取锁。如果锁空闲,则获取成功并继续执行;如果锁已被占用,则阻塞等待。
    • release():释放锁。允许其他等待的线程获取它并进入临界区。
  • 特性:互斥量具有互斥性(同一时刻仅一个线程持有锁)和原子性acquirerelease操作本身是不可中断的)。

2、Python 中的互斥量实现

Python 标准库threading模块提供了两种主要的锁类型:

2.1 、threading.Lock(普通锁/互斥锁)

这是最基本的互斥量实现。

  • 获取与释放:必须显式调用acquire()release()
  • 不可重入:同一个线程在持有锁的情况下再次调用acquire()会导致死锁(Deadlock),因为该线程会无限等待自己释放锁。
importthreading# 共享资源counter=0# 创建一个互斥锁lock=threading.Lock()defincrement():globalcounterfor_inrange(1000000):lock.acquire()# 获取锁,进入临界区counter+=1# 修改共享资源lock.release()# 释放锁,退出临界区# 创建并启动两个线程thread1=threading.Thread(target=increment)thread2=threading.Thread(target=increment)thread1.start()thread2.start()thread1.join()thread2.join()print(f"Final counter value:{counter}")# 应该输出 2000000

2.2 、threading.RLock(可重入锁/递归锁)

RLockLock的基础上增加了可重入(Reentrant)特性。

  • 可重入性:同一个线程可以多次成功调用acquire()而不会阻塞。线程内部需要维护一个计数器,记录获取锁的次数。
  • 释放:线程必须调用release()的次数与其调用acquire()的次数相匹配,锁才会被真正释放给其他线程。
  • 适用场景:适用于可能递归调用或需要嵌套加锁的函数。
importthreading rlock=threading.RLock()deffunc1():withrlock:# 第一次获取锁print("Inside func1, first level")func2()# 调用 func2, 需要再次获取同一把锁deffunc2():withrlock:# 第二次获取锁 (同一个线程)print("Inside func2, second level")# 同一个线程内嵌套调用func1()

3、使用with语句管理锁

为了避免忘记释放锁(尤其是在异常发生时),强烈推荐使用with语句。锁对象支持上下文管理协议。

  • 进入with块时自动获取锁
  • 退出with块时(无论正常退出还是异常退出)自动释放锁
defincrement_safe():globalcounterfor_inrange(1000000):withlock:# 自动获取锁counter+=1# 自动释放锁

4、互斥量与条件变量 (threading.Condition)

条件变量通常与互斥锁一起使用,用于线程间的更复杂协调(例如生产者-消费者模型)。条件变量内部关联着一个锁(通常是RLock)。

  • wait():释放关联的锁,并使线程进入等待状态,直到被其他线程唤醒。
  • notify()/notify_all():唤醒一个或所有在该条件变量上等待的线程。被唤醒的线程会尝试重新获取关联的锁。

5、注意事项与潜在问题

  • 死锁(Deadlock):当两个或多个线程相互等待对方持有的锁时,所有线程都无法继续执行。避免死锁需要谨慎设计锁的获取顺序,或使用带超时的锁。
  • 锁粒度:锁的粒度(保护范围)太粗会降低并发性能;太细会增加管理复杂度并可能引入新的错误。需要权衡。
  • 性能开销:获取和释放锁本身有一定的开销。过度使用锁会降低多线程程序的性能。
  • 超时机制acquire(timeout=seconds)可以设置超时时间,避免无限期阻塞。

6、其他同步工具 (简要提及)

  • 信号量 (threading.Semaphore):允许多个线程(数量上限由信号量值决定)同时访问资源。
  • 事件 (threading.Event):用于线程间简单的通知机制。
  • 栅栏 (threading.Barrier):用于让多个线程在某个点等待,直到所有线程都到达后才一起继续执行。

7、总结

互斥量 (Lock,RLock) 是 Python 多线程编程中控制共享资源访问、防止竞态条件的基本工具。使用with语句可以安全便捷地管理锁的获取和释放。理解互斥量的工作原理、区别以及潜在的死锁风险,对于编写正确、高效的多线程 Python 程序至关重要。根据实际场景选择LockRLock,并考虑与其他同步原语(如条件变量)配合使用以满足更复杂的需求。

二、代码示例

1、源码分享

importthreadingimporttime# 共享变量(多个线程都会改它)shared_num=0# 创建互斥锁(核心)lock=threading.Lock()# 线程要执行的函数defadd_task():globalshared_numfor_inrange(100000):# ========== 加锁开始 ==========lock.acquire()# 上锁try:# 同一时间只有一个线程能执行这里shared_num+=1finally:lock.release()# 释放锁(必须执行)# ========== 加锁结束 ==========# 更推荐写法:自动加锁/解锁(with 语句)# def add_task():# global shared_num# for _ in range(100000):# with lock: # 自动上锁,离开自动解锁# shared_num += 1if__name__=="__main__":# 创建2个线程t1=threading.Thread(target=add_task)t2=threading.Thread(target=add_task)t1.start()t2.start()t1.join()t2.join()# 正确结果一定是 200000print("最终结果:",shared_num)

2、运行结果

C:\Users\徐鹏\Desktop\55\.venv\Scripts\python.exe C:\Users\徐鹏\Desktop\55\main.py 最终结果:200000进程已结束,退出代码为0

http://www.jsqmd.com/news/673062/

相关文章:

  • 软考架构设计师论文 —— 论云原生架构及其应用
  • 类的动态加载与漏洞利用
  • Flink Watermark 设计分析
  • H.264编码实战:从I帧到B帧的压缩魔法与避坑指南
  • 从零到一:手把手教你用TensorFlow 2.0搭建BiSeNetV2,实现Cityscapes语义分割
  • python cdk8s
  • 如何深度掌控Ryzen性能:SMUDebugTool硬件调试终极指南 [特殊字符]
  • 【5G通信】大规模MIMO技术5G网络上下行功率优化【含Matlab源码 15359期】
  • 别再死记硬背了!用Cesium加载倾斜摄影,搞懂3D Tiles的‘外包盒’和‘几何误差’就够了
  • 2026上海美术高中双轨升学深度测评:从品牌到路径的客观对比指南 - 商业小白条
  • 还在为黑苹果配置发愁?OCAuxiliaryTools 让复杂配置变得像搭积木一样简单
  • 多因子AI定价模型:局势不确定性冲击下黄金跳空波动与再定价机制解析
  • ADS-B Receiver 系统逐步安装部署指南
  • 从合并日志到游戏对象管理:实战盘点C++ list::splice的5个高频应用场景
  • 别再搞混了!彻底搞懂nav_msgs::OccupancyGrid里的origin、resolution和width/height
  • 别再让PCIe设备‘私聊’了:手把手教你配置ACS服务,堵上P2P传输的安全漏洞
  • CoreXY架构革命:Voron 2.4如何实现300mm/s高速打印的极致精度
  • 从随机数据到平滑曲线:用PCHIP算法在MATLAB中玩转数据插值(保姆级教程)
  • 录播姬终极指南:3分钟快速上手B站直播录制工具
  • 兰亭妙微设计|告别千篇一律:从闲鱼、嘀嗒、饿了么案例看UI设计的差异化巧思
  • Qt 中的队列解析
  • 光口与电口的感性认识
  • 如何让电脑风扇变聪明:FanControl终极静音散热配置指南
  • 13 ControlNet 到底是什么:在 ComfyUI 里理解“可控生成”的关键一步
  • Twine App Builder:让网页游戏变身桌面应用的魔法工具
  • 2026年SCI/EI论文AI润色新突破
  • 从MATLAB仿真到FPGA上板:一个8Mbps通信系统的成形滤波器全链路实现
  • Pybind11实战:在Visual Studio里为你的C++算法快速生成Python接口
  • 别再瞎调PLL了!手把手教你用STM32CubeMX配置STM32F411的100MHz系统时钟(HSI/HSE对比实测)
  • 【5G通信】5G通信超密集网络多连接负载均衡和资源分配【含Matlab源码 15361期】