异步函数抛异常本身不直接导致死锁,但若锁未用async with保护、I/O混入临界区、存在循环await或未捕获异常中断流程,则会引发静默挂起等效死锁;须统一用async with lock、移出I/O、拆分依赖、加超时与日志监控。

异步函数里抛出异常,本身不会直接导致死锁;但若异常处理不当,会让锁、资源或依赖链卡在中间状态,从而引发逻辑等效的死锁——比如协程永远停在 await 某个不 resolve 的 Promise,或锁没释放、初始化没完成却无报错。
确保锁操作始终成对且异常安全
asyncio.Lock 或自定义异步锁必须 acquire 和 release 在同一协程内配对。手动调用 await lock.acquire() 后,一旦中间抛异常,lock.release() 就可能被跳过,锁永久占用。
- 一律用
async with lock:,它自动在退出时(无论正常还是异常)调用 release - 不要在
async with lock:块里 await 外部 I/O(如 HTTP 请求、数据库查询),否则锁会被长时间持有,其他协程全被堵住 - 若必须串行化 I/O 操作,改用
asyncio.Semaphore(1),语义更清晰,也方便后续扩容
避免 await 循环依赖与未捕获异常中断流程
两个 async 函数互相 await,表面看是逻辑错误,实际会表现为“无声挂起”:没有报错,但所有调用都卡住。
- 初始化阶段尤其危险:A.init() 等 B.init(),B.init() 又等 A.init()
- 解法不是加 try/catch,而是拆分依赖,用协调器统一启动,或引入超时保护:
await asyncio.wait_for(initB(), timeout=3) - 所有关键 await 都应包裹 try/catch,尤其是网络、文件、数据库调用——未捕获异常可能让上层流程静默中断,间接导致后续 await 永远等不到结果
监控与兜底:让挂起暴露出来,而不是沉默
死锁类问题最难调试,正因它不报错、不崩溃,只“变慢”或“不响应”。
- 对超过阈值(如 2 秒)未完成的 Promise / Task 主动 reject 或 cancel,并记录日志
- 在入口和关键节点打时间戳日志,对比进入/退出,快速定位卡点
- 浏览器可用 Performance 面板查长任务;Node.js 可启用
--trace-warnings或 Promise 生命周期追踪
不复杂但容易忽略

















