一个加锁的函数里又调了另一个同样加锁的函数,进程没有任何报错,堆栈停在 acquire 那一行,CPU 占用一直是零。第一次遇到会往复杂处想,其实漏掉的多是个小前提。
lock = threading.RLock()
with lock:
with lock:
pass # 换成普通 Lock 会卡死
普通 Lock 不记录持有者,同一线程第二次 acquire 也要等锁释放,而锁正被自己拿着,于是永久等待,并且不抛异常,只能靠外部超时打断。这一点先确认清楚,后面就不用反复试。
「Lock 重入死锁」相关的验证我通常做两遍,改完一次、隔天再看一次,确认不是临时生效。临时改动用注释写上日期和原因,过两周自己都记不清为什么这么写。
动手前把原文件复制一份带时间戳,出问题立刻换回去,比现场分析快得多。
具体做法是,同一线程可能重入的地方换成 RLock,别用普通锁顶着;把加锁范围收窄,只保护真正被共享的那几行代码,别整段包住;用带超时的 acquire 去拿锁,先把卡住的位置暴露出来再改结构。
改完照着核一遍,嵌套两层调用都能正常返回,线程不会一直停在 acquire 上,也不会留下卡死的堆栈。
