死锁是指两个或多个线程互相等待对方持有的锁而无法继续执行。常见于python多线程中未按统一顺序获取多个lock/rlock,或忘记释放锁,导致循环等待。

什么是死锁,为什么会出现
死锁是指两个或多个线程互相等待对方持有的锁,导致所有线程都无法继续执行。在 Python 中,常见于多线程使用 threading.Lock 或 RLock 时,未按统一顺序获取多个锁,或忘记释放锁。
典型死锁场景还原
比如线程 A 先获取 lock1 再尝试获取 lock2,而线程 B 正好相反:先 lock2 后 lock1。一旦 A 持有 lock1、B 持有 lock2,双方都在等对方释放,就卡住了。
代码示意:
lock1 = threading.Lock()
lock2 = threading.Lock()
<p>def thread_a():
with lock1:
time.sleep(0.1) # 让B有机会先拿到lock2
with lock2: # 等lock2 → 但B已占着
print("A done")</p><p>def thread_b():
with lock2:
time.sleep(0.1)
with lock1: # 等lock1 → 但A已占着
print("B done")</p>排查死锁的实用方法
-
加超时锁:用
lock.acquire(timeout=2)替代with lock:,超时抛出异常,能快速暴露卡点 - 记录锁生命周期:在 acquire / release 前后打日志,包含线程名、锁标识、时间戳,再用日志分析谁持有什么、等什么
- 用 threading.settrace 钩住锁调用(进阶):拦截所有 acquire/release,构建运行时锁依赖图,发现循环等待
- 避免嵌套锁:一个函数尽量只持有一个锁;如必须操作多个资源,改用单个粗粒度锁,或用锁排序协议
锁顺序设计的核心原则
- 全局唯一编号:给每个锁对象分配固定整数 ID(如 hash(lock) % 10000),所有线程严格按 ID 升序获取锁
-
封装成安全获取函数:写一个
acquire_all(*locks),内部自动排序并依次 acquire,避免手误 - 禁止反向释放:释放顺序无需与获取严格相反,但不要在持有小ID锁时去获取更大ID锁——这会破坏顺序性
- 资源分层建模:把锁对应到业务层级(如“用户表锁”










