asyncio.create_task 返回 Task 对象,直接 await 会同步执行而丧失并发性;正确做法是先创建再 await,或用 asyncio.gather 并发等待;需后台运行时才必须用 create_task。

asyncio.create_task 为什么不能直接 await?
因为 asyncio.create_task 返回的是一个 Task 对象,不是协程对象。直接 await create_task(...) 虽然语法合法,但会失去并发意义——它等价于同步执行该协程,任务刚创建就立刻阻塞等待完成,和不用 create_task 没区别。
正确做法是先创建任务,再在合适时机 await 它(比如用 asyncio.gather 或单独 await task)。
- 错误写法:
await asyncio.create_task(some_coro()) - 正确写法:
task = asyncio.create_task(some_coro()),后续await task - 若需并发等待多个任务,优先用
await asyncio.gather(task1, task2),而非分别await
什么时候必须用 create_task 而不是直接 await 协程?
当你需要「启动后不立即等待」,让协程在后台并发运行时,就必须用 create_task。典型场景包括:定时轮询、事件监听、预加载、或避免阻塞主流程。
例如,在 Web 请求处理中提前拉取非关键数据:
async def handle_request():
# 启动后台任务,不阻塞响应
background_task = asyncio.create_task(fetch_analytics_data())
<pre class="brush:php;toolbar:false;"># 先返回核心响应
response = await generate_main_content()
# 可选:稍后检查后台任务是否完成,但不强求
try:
await asyncio.wait_for(background_task, timeout=0.5)
except asyncio.TimeoutError:
pass # 超时就放弃,不影响主流程
return response- 直接
await fetch_analytics_data()会让用户多等几百毫秒 -
create_task把它扔进事件循环,立刻继续执行 - 注意:未被
await的任务如果抛出异常,会在事件循环结束时以Task exception was never retrieved警告输出,可能掩盖问题
create_task 和 ensure_future 有什么实际区别?
在绝大多数日常使用中,asyncio.create_task 是 ensure_future 的更安全替代。Python 3.7+ 推荐只用 create_task。
-
create_task只接受协程对象,类型更严格,不会意外把普通函数或 Future 当作协程传入 -
ensure_future更底层,能处理协程、Future、甚至asyncio.Task实例,但容易误用(比如传入已运行的 Task) - 性能上无差异,但
create_task有额外校验,能早暴露错误 - 如果你看到别人用
ensure_future(coroutine),直接换成create_task(coroutine)即可
忘记 await 未完成的 Task 会导致什么?
任务对象本身会被垃圾回收,但它内部的协程如果正在等待 I/O(比如网络请求),这个等待不会被取消,资源(如连接、内存)可能泄漏,且异常永远不会被传播出来。
常见表现:Task exception was never retrieved 警告 + 程序退出前卡顿几秒。
- 确保每个
create_task都有明确的await路径,或显式调用task.cancel() - 在
try/finally或async with中管理生命周期更稳妥 - 调试时可用
asyncio.all_tasks()查看当前还有哪些活跃任务 - 生产环境建议用
asyncio.create_task(..., name="xxx")命名任务,便于日志追踪
真正麻烦的不是语法怎么写,而是任务生命周期没对齐——创建了却没人收尾,比写错一行 await 更难排查。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











