asyncio.all_tasks() 返回当前事件循环中所有未完成的Task对象,包括运行中、已调度未执行、已取消未清理的任务,但不包含普通协程或未包装的协程。

asyncio.all_tasks() 返回的是什么任务
asyncio.all_tasks() 获取的是当前事件循环中所有未完成的 Task 对象,包括正在运行、已调度但未开始、甚至已取消但尚未被清理的任务——但它**不包含普通协程对象(coroutine)或未被 create_task() / ensure_future() 包装的协程**。
常见误解是认为它能列出“所有正在执行的 async 函数”,其实不然:只有被显式提交到事件循环的任务才会被纳入。比如直接 await some_coro() 不会产生一个可被 all_tasks() 捕获的独立任务。
- 如果你在
asyncio.run()内部调用,通常只能看到你手动创建的Task和后台的asyncio._RunUntilCompleteTask - 在自管理的事件循环(如
loop.run_forever())中,还可能包含系统级任务(如信号处理、调试器钩子) - 注意:Python 3.12+ 中
asyncio.all_tasks()默认只返回当前线程关联的事件循环任务;跨线程调用需显式传入loop参数
如何安全地遍历并识别自己的任务
直接遍历 asyncio.all_tasks() 返回的集合容易误操作(比如对系统任务调用 cancel()),推荐结合 get_name() 或自定义标识来过滤。
最稳妥的方式是在创建任务时显式命名:
立即学习“Python免费学习笔记(深入)”;
task = asyncio.create_task(some_coro(), name="fetch-user-123")
之后就可以这样筛选:
- 用
task.get_name().startswith("fetch-")匹配业务任务 - 避免依赖
task.get_coro().__name__—— 协程名可能重复,且匿名 lambda 协程会返回<lambda> - 不要用
id(task)做长期标识,对象销毁后 ID 可能复用
为什么有时候 all_tasks() 看不到刚 create_task() 的任务
典型现象:调用 asyncio.create_task() 后立刻 print(asyncio.all_tasks()),却发现列表里没有它。这不是 bug,而是因为任务创建和调度存在微小时间差。
-
create_task()是同步函数,只负责把协程包装成Task并注册进事件循环,但实际执行时机由下一次事件循环迭代决定 - 若你在主线程同步上下文中调用
all_tasks()(比如刚create_task()完还没await任何东西),该任务可能尚未被事件循环真正“看到” - 验证方法:加一句
await asyncio.sleep(0)再查,通常就能看到
替代方案:比 all_tasks() 更精准的监控方式
如果目标是跟踪自己启动的异步工作流,硬啃 all_tasks() 往往绕远路。更可控的做法是维护一个显式的任务容器:
running_tasks = set() <p>async def tracked_task(coro): task = asyncio.create_task(coro) running_tasks.add(task) try: return await task finally: running_tasks.discard(task)</p><h1>启动时:</h1><p>asyncio.create_task(tracked_task(fetch_data()))
这种模式规避了全局扫描开销,也避免了误触系统任务的风险。尤其适合长周期服务(如 WebSocket 连接管理、定时轮询)中做生命周期归档或超时强制清理。
真正难的不是“怎么拿到任务列表”,而是“怎么定义‘我的任务’的边界”——名字、作用域、生命周期归属,这些得靠代码结构来约定,而不是靠反射去猜。


















