Future 是未绑定事件循环的“结果占位符”,仅作状态容器;Task 是其子类,绑定协程并自动注册到事件循环,支持 await。95% 场景用 Task,Future 仅用于跨线程、阻塞 I/O 封装等桥接场景。

Future 不能直接 await,否则会报 RuntimeError: Task got bad yield: <future pending></future>;Task 是 Future 的子类,专为协程调度设计,可被事件循环自动驱动。
Future 是什么:一个未绑定事件循环的“结果占位符”
Future 是 asyncio 底层的同步/异步桥接对象,本质是状态容器(PENDING/FINISHED/CANCELLED),只负责存结果、发回调、响应 done() 或 result() 调用。它不包含协程体,也不注册进事件循环。
常见错误现象:
- 直接
await asyncio.Future()→ 报错RuntimeError: Task got bad yield - 在非主线程调用
set_result()→ 可能引发竞态或事件循环未响应
使用场景有限,典型包括:
立即学习“Python免费学习笔记(深入)”;
- 跨线程通信(配合
loop.call_soon_threadsafe()) - 手动封装阻塞 I/O(如将
threading.Thread结果写入 Future) - 测试中模拟异步完成(比如 mock 一个延迟返回的 Future)
Task 是什么:Future 的子类,也是协程的“调度实体”
Task 继承自 Future,但关键差异在于它绑定了协程对象,并在创建时就注册进当前事件循环——这意味着它能被自动推进、暂停、取消,且天然支持 await。
参数与行为差异:
快速生成专业的 Python 脚本和应用代码。一键创建完整项目结构,支持CLI、API、爬虫、Bot、Django等多种项目类型,包含完整的项目结构、配置文件、依赖管理、测试、README和文档。
-
asyncio.create_task(coro):推荐方式,必须在运行中的事件循环内调用 -
asyncio.ensure_future(coro):兼容旧代码,对协程、Future、Task 都做适配,但语义不如 create_task 清晰 -
loop.create_task(coro):底层等价,适合需显式控制 loop 的场景
性能影响很小,但语义明确性直接影响可维护性。别用 ensure_future 包裹普通 Future 对象——它不会帮你“升级”成可 await 的东西。
为什么 set_result() 不能乱用:线程安全与状态约束
set_result() 只能在 Future 处于 PENDING 状态时调用,且必须由同一线程或通过 loop.call_soon_threadsafe() 触发。否则可能静默失败,或导致事件循环卡住。
容易踩的坑:
- 在子线程里直接调用
future.set_result("ok")→ 结果无法被 await 捕获 - 重复调用
set_result()或对已done()的 Future 调用 → 抛InvalidStateError - 用
set_result()设置了值,却忘了await对应的 Task → 程序逻辑断链,无报错但行为异常
示例修正写法:
def thread_worker(future):
time.sleep(1)
# ❌ 错误:直接 set_result
# future.set_result("done")
<pre class="brush:php;toolbar:false;"># ✅ 正确:线程安全投递
loop = asyncio.get_running_loop()
loop.call_soon_threadsafe(future.set_result, "done")
实际开发中该选哪个:95% 的情况用 Task,Future 仅用于特定桥梁场景
日常协程编排、并发控制、错误传播,全部走 create_task() 即可。Future 的存在价值不在“替代 Task”,而在“让非协程逻辑接入事件循环”——比如把数据库连接池的回调、信号处理函数、C 扩展的异步通知,转成 async/await 可消费的形式。
复杂点往往藏在边界处:当你需要从线程、信号、C 回调、或第三方库(如某些老版 aiohttp 插件)拿到结果并喂给协程时,Future 才真正不可替代。这时候务必检查状态、确保线程安全、避免手动管理生命周期。


















