未await的任务不会立即内存泄漏,但存在未处理异常静默丢失、资源无法释放、无限任务阻塞事件循环三类风险;应跟踪任务、适时await或加异常/清理逻辑,并设置全局异常处理器。

如果用 asyncio.create_task() 创建了任务,但从未对它 await、也未通过其他方式(如 asyncio.wait()、asyncio.gather() 或显式调用 task.result()/task.exception())等待或检查其结果,这个任务**不会立即“泄漏”内存**,但它可能带来三类实际风险:未处理的异常、资源滞留、以及难以调试的行为。
未捕获的异常会静默丢失
当一个被创建但无人 await 的任务内部抛出异常时,该异常**不会传播到事件循环之外,也不会中断程序**。默认情况下,asyncio 会在任务结束时把未处理的异常记录为 warning(Python 3.8+),例如:
Task exception was never retrieved但如果你没开日志、没监听 asyncio.get_event_loop().set_exception_handler(),这个错误就彻底消失了——你根本不知道任务已崩溃。
资源可能无法及时释放
任务中若打开了文件、网络连接、数据库游标或使用了异步上下文管理器(async with),而任务本身又没被 await 或取消,那么:
-
__aexit__不会被触发 → 连接不关闭、文件不刷新 - 依赖
finally块或异步清理逻辑的代码不会执行 - 比如:
async with aiohttp.ClientSession() as session:中的 session 可能长期占用连接池 slot
任务对象本身不会“内存泄漏”,但可能阻塞事件循环终止
任务对象本身会被 Python 垃圾回收(只要没有强引用),但要注意:
- 正在运行的任务会持续调度,直到完成或被取消
- 如果任务是无限循环(如
while True: await asyncio.sleep(1))且未被引用、也未被取消,它仍会一直跑下去 —— 即使你丢了 task 变量,只要它还在事件循环里活跃,就不会被回收 - 程序退出时(如
asyncio.run()结束),未完成的任务会被自动 cancel 并等待(默认 1 秒超时),但如果任务在 cancel 后卡在某个不可中断的 IO 或死循环里,可能导致主程序 hang 住或报错
如何避免这类问题
- 总是设法跟踪你创建的任务:存入列表、集合,或用
asyncio.create_task(..., name="xxx")命名便于调试 - 在合适时机
await它,或用asyncio.gather(*tasks)统一等待 - 如果任务是“fire-and-forget”,确保它内部有完备的异常处理和资源清理(比如包在
try/except/finally或async with里) - 启用全局异常处理器来兜底:
loop.set_exception_handler(lambda loop, ctx: logging.error("Uncaught task error", exc_info=ctx.get('exception')))










