
Python 多线程死锁排查核心是定位“谁在等谁”——关键看线程阻塞点、锁的持有与请求顺序。死锁本身不报错,程序会静默卡住,所以得靠日志、堆栈和工具主动挖。
看线程堆栈,确认阻塞位置
程序卡住时,用 Ctrl+C 触发 KeyboardInterrupt,Python 会打印所有线程当前调用栈(含 Lock.acquire()、Rlock.acquire() 等阻塞调用)。重点找:
- 多个线程都停在
acquire()上,且参数是同一个 Lock 对象(ID 相同) - 线程 A 持有 lock1 正在等 lock2,线程 B 持有 lock2 正在等 lock1
也可用 threading.enumerate() + thread.ident 配合 sys._current_frames() 主动抓取各线程帧,适合无法中断的长期运行服务。
加锁日志,追踪锁的生命周期
在每次 acquire() 前和 release() 后打日志,记录线程名、锁 ID、时间戳、调用位置:
立即学习“Python免费学习笔记(深入)”;
- 用自定义封装类(如
LoggedLock),重写acquire/release - 日志中包含
threading.current_thread().name和id(lock),避免混淆不同锁实例 - 典型死锁线索:A 线程 acquire lock1 → acquire lock2 卡住;B 线程 acquire lock2 → acquire lock1 卡住
检查锁的申请顺序是否一致
死锁最常见原因是多个线程以不同顺序获取多个锁。例如:
- 函数 f() 先 lock_a 再 lock_b
- 函数 g() 先 lock_b 再 lock_a
只要两个函数被不同线程并发调用,就可能死锁。解决方法是全局约定顺序,比如按锁对象 ID 升序获取:
locks = sorted([lock_a, lock_b], key=id)<br>for l in locks: l.acquire()
用 threading.settrace 或第三方工具辅助
对复杂场景,可启用线程级 trace:
-
threading.settrace()可监控每个线程的函数进入/退出,配合锁操作打点 - 使用 deadlock-detector 库(pip install deadlock-detector),它会在后台周期性扫描锁依赖图,发现环路即报警
- 生产环境可用
py-spy record -o profile.svg --pid $PID抓火焰图,直观看到哪些线程长时间停在 acquire 调用上








