“task was destroyed but it is pending!”是python 3.11+的runtimewarning,表明task被gc销毁时仍处于pending状态,暴露任务失控、资源滞留风险;常见于create_task()后未保存引用、异步上下文内漏管任务或shield后遗忘引用,开启asyncio debug模式可定位创建点与挂起行。

这是 asyncio 最典型的挂起任务泄漏信号,不是“异常”而是资源失控告警:它说明某个 Task 对象被 Python 垃圾回收器销毁时,仍处于 pending(未完成)状态——既没执行完,也没被显式取消或 await 过。
Task was destroyed but it is pending! 的真实含义
这个提示不是运行时错误,而是 Python 3.11+ 在事件循环退出或 Task 被 GC 时发出的 RuntimeWarning。它暴露的是一个更严重的问题:你失去了对这个任务的控制权。
- 任务可能还在后台运行(比如卡在某个
await上),但你再也拿不到它的返回值、无法捕获它的异常、也不能主动 cancel 它 - 任务内部持有的资源(如打开的 socket、数据库连接、内存缓存)可能长期滞留,直到底层超时或 GC 强制清理
- 常见于
create_task()后直接 return、局部变量保存后函数退出、或忘记把 task 加入监控集合(如asyncio.all_tasks())
最常触发的三个代码模式
以下写法看似无害,实则埋雷:
-
asyncio.create_task(some_coro())后不保存引用也不 await —— 任务对象一出作用域就被丢弃 - 在异步生成器或
__aenter__/__aexit__中启动 task,但没确保它在上下文退出前完成或被 cancel - 用
asyncio.shield()包裹任务后直接扔掉 task 引用 ——shield只防取消,不防遗忘
为什么 debug=True 能定位到根因
默认情况下,Python 只报错文字,不告诉你哪个协程、在哪一行挂起。开启调试模式后:
- 环境变量:
PYTHONASYNCIODEBUG=1 python script.py - 代码中:
asyncio.run(main(), debug=True)或loop.set_debug(True)(需在 loop 启动前) - 一旦有 pending task 被销毁,会打印完整 traceback,包括:
Task创建位置(create_task调用点)和当前挂起的await行 —— 这才是修复依据
别忽略线程与事件循环绑定问题
如果你在子线程里调用了 asyncio.get_running_loop() 或直接 create_task(),很可能根本没拿到主线程的 loop,任务实际跑在了一个孤立、无人管理的 loop 里。这种场景下,即使开了 debug 模式,也会看到类似警告:coroutine object created in thread X but awaited in thread Y。此时任务从创建起就脱离了主事件循环的生命周期管理,销毁时必然报 Task was destroyed。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











