
asyncio.create_task 什么时候必须用,什么时候其实不用
直接说结论:asyncio.create_task 的核心作用是「把一个协程对象立刻调度进事件循环,并返回一个 Task 对象供后续控制」。它不是启动协程的唯一方式,也不是所有 await 场景都需要它。
常见误用:在 async def 函数里,对一个协程直接 await func() 就够了,根本不需要先 create_task 再 await —— 这样做不仅多余,还多了一层任务调度开销。
- 必须用:你想让协程「并发执行、不阻塞当前协程」,比如发多个 HTTP 请求但不想等第一个返回才发第二个
- 不该用:你只是想顺序调用、等待结果,
await func()更清晰、更轻量 - 容易踩坑:在未运行事件循环的上下文(如普通 Python 脚本顶层、同步函数里)调用
create_task,会报RuntimeError: no running event loop
create_task 后不 await,任务就“丢”了吗
不会“丢”,但可能被静默取消或出错后没人处理。
create_task 返回的 Task 对象如果一直没人 await、没被加入 asyncio.gather、也没被显式 cancel() 或检查状态,它会在后台跑完——但它的异常不会冒泡到主线程,也不会中断其他协程。这就像开了个后台线程却没管它的报错。
- 典型问题:后台日志上传协程出错了,但主逻辑完全感知不到,日志就断了
- 安全做法:至少把它加进
asyncio.create_task(..., name="upload-logs")并在合适位置用asyncio.all_tasks()或日志监控其状态 - 更稳妥:用
asyncio.create_task(...).add_done_callback(...)捕获完成或异常 - 注意:Python 3.11+ 中,未被引用的
Task可能被垃圾回收并触发警告(ResourceWarning: Task was destroyed but it is pending)
和 asyncio.ensure_future 的区别,别混着用
asyncio.create_task 是 Python 3.7+ 推荐的、专用于调度协程为任务的接口;asyncio.ensure_future 更老、更宽泛,能处理协程、Future、甚至 Task 实例,但语义模糊。
- 如果你传的是协程对象,
ensure_future内部其实就调用了create_task(在有运行循环时) - 但
ensure_future在没有运行循环时会尝试创建新循环,行为不可控;而create_task明确要求当前已有运行循环,失败即报错,更利于提前发现问题 - 参数差异:
create_task支持name参数(便于调试),ensure_future不支持 - 性能上无实质差别,但可读性和意图表达上,
create_task更明确
Task 对象本身不是“线程”,别指望它跨线程运行
asyncio.create_task 创建的 Task 严格绑定在当前事件循环线程中,它不能自动跳到其他线程执行。想跑 CPU 密集型操作?它只会卡住整个异步循环。
- 错误认知:“我用
create_task就算异步了,CPU 计算也能不阻塞” - 真实情况:
task里调用time.sleep(5)或纯 Python 循环,照样阻塞整个asyncio程序 - 正确解法:CPU 密集任务必须用
loop.run_in_executor托管到线程池/进程池,再await返回的Future - 兼容性提醒:Windows 上默认事件循环是
ProactorEventLoop,某些 executor 场景需显式指定ThreadPoolExecutor
最常被忽略的一点:任务的生命周期和事件循环强绑定。一旦循环关闭(比如 asyncio.run() 结束),所有未完成的 Task 都会被取消,且不会等它们自然退出——哪怕你刚 create_task 了一个长耗时上传,也可能被无声中断。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











