python 互斥量详解

张开发
2026/4/20 20:46:18 15 分钟阅读

分享文章

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()释放锁。允许其他等待的线程获取它并进入临界区。特性互斥量具有互斥性同一时刻仅一个线程持有锁和原子性acquire和release操作本身是不可中断的。2、Python 中的互斥量实现Python 标准库threading模块提供了两种主要的锁类型2.1 、threading.Lock(普通锁/互斥锁)这是最基本的互斥量实现。获取与释放必须显式调用acquire()和release()。不可重入同一个线程在持有锁的情况下再次调用acquire()会导致死锁Deadlock因为该线程会无限等待自己释放锁。importthreading# 共享资源counter0# 创建一个互斥锁lockthreading.Lock()defincrement():globalcounterfor_inrange(1000000):lock.acquire()# 获取锁进入临界区counter1# 修改共享资源lock.release()# 释放锁退出临界区# 创建并启动两个线程thread1threading.Thread(targetincrement)thread2threading.Thread(targetincrement)thread1.start()thread2.start()thread1.join()thread2.join()print(fFinal counter value:{counter})# 应该输出 20000002.2 、threading.RLock(可重入锁/递归锁)RLock在Lock的基础上增加了可重入Reentrant特性。可重入性同一个线程可以多次成功调用acquire()而不会阻塞。线程内部需要维护一个计数器记录获取锁的次数。释放线程必须调用release()的次数与其调用acquire()的次数相匹配锁才会被真正释放给其他线程。适用场景适用于可能递归调用或需要嵌套加锁的函数。importthreading rlockthreading.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:# 自动获取锁counter1# 自动释放锁4、互斥量与条件变量 (threading.Condition)条件变量通常与互斥锁一起使用用于线程间的更复杂协调例如生产者-消费者模型。条件变量内部关联着一个锁通常是RLock。wait()释放关联的锁并使线程进入等待状态直到被其他线程唤醒。notify()/notify_all()唤醒一个或所有在该条件变量上等待的线程。被唤醒的线程会尝试重新获取关联的锁。5、注意事项与潜在问题死锁Deadlock当两个或多个线程相互等待对方持有的锁时所有线程都无法继续执行。避免死锁需要谨慎设计锁的获取顺序或使用带超时的锁。锁粒度锁的粒度保护范围太粗会降低并发性能太细会增加管理复杂度并可能引入新的错误。需要权衡。性能开销获取和释放锁本身有一定的开销。过度使用锁会降低多线程程序的性能。超时机制acquire(timeoutseconds)可以设置超时时间避免无限期阻塞。6、其他同步工具 (简要提及)信号量 (threading.Semaphore)允许多个线程数量上限由信号量值决定同时访问资源。事件 (threading.Event)用于线程间简单的通知机制。栅栏 (threading.Barrier)用于让多个线程在某个点等待直到所有线程都到达后才一起继续执行。7、总结互斥量 (Lock,RLock) 是 Python 多线程编程中控制共享资源访问、防止竞态条件的基本工具。使用with语句可以安全便捷地管理锁的获取和释放。理解互斥量的工作原理、区别以及潜在的死锁风险对于编写正确、高效的多线程 Python 程序至关重要。根据实际场景选择Lock或RLock并考虑与其他同步原语如条件变量配合使用以满足更复杂的需求。二、代码示例1、源码分享importthreadingimporttime# 共享变量多个线程都会改它shared_num0# 创建互斥锁核心lockthreading.Lock()# 线程要执行的函数defadd_task():globalshared_numfor_inrange(100000):# 加锁开始 lock.acquire()# 上锁try:# 同一时间只有一个线程能执行这里shared_num1finally:lock.release()# 释放锁必须执行# 加锁结束 # 更推荐写法自动加锁/解锁with 语句# def add_task():# global shared_num# for _ in range(100000):# with lock: # 自动上锁离开自动解锁# shared_num 1if__name____main__:# 创建2个线程t1threading.Thread(targetadd_task)t2threading.Thread(targetadd_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

更多文章