future不是task,不能直接await;它是底层回调容器,需ensure_future包装或create_task驱动,set_result()仅限未完成状态且线程安全需call_soon_threadsafe。

Future对象不是Task,不能直接await
Python的Future是底层异步回调容器,本身不参与事件循环调度;而Task是继承自Future的协程封装体,能被asyncio.run()或loop.create_task()自动驱动。直接对普通Future使用await会报RuntimeError: Task got bad yield: <future pending></future>——因为没绑定到事件循环,也没协程体可挂起。
- 只有
asyncio.ensure_future()或loop.create_task()创建的才是可await的Task - 手动创建的
asyncio.Future()必须显式用loop.call_soon()或loop.create_task()触发其完成逻辑 -
Future没有__await__方法(除非被ensure_future包装过),所以不能直接await
set_result()只能调用一次,且必须在未完成状态下
set_result()是Future的“写入接口”,但它是单向状态机操作:一旦Future进入done()状态(无论成功/失败),再调用就会抛出InvalidStateError: invalid state。这和Task不同——Task的结果由协程返回值自动设置,不允许外部手动set_result()。
- 检查是否可设:
if not fut.done(): fut.set_result("ok") - 多线程环境中调用
set_result()必须通过loop.call_soon_threadsafe(),否则可能引发竞态或循环崩溃 - 不要在协程里对同一个
Future反复set_result(),哪怕加了if done()判断也不安全——它可能被其他路径提前完成
Future和Task混用时,done()和result()行为差异明显
Future.done()只反映“是否已设置结果或异常”,而Task.done()还包含协程是否已彻底退出(包括被取消)。更关键的是:Future.result()会立即返回值或抛出异常;Task.result()在未完成时会raise InvalidStateError,但await task会自动等待——这是最常混淆的点。
-
fut = asyncio.Future(); fut.done()→False;task = asyncio.create_task(coro()); task.done()→ 初始也是False,但语义不同 -
fut.result()在未完成时阻塞当前线程(如果在同步上下文)或抛InvalidStateError(如果在异步上下文且未await) -
task.result()永远不阻塞,只读取最终状态;要等结果必须await task或loop.run_until_complete(task)
什么时候该用Future而不是Task
绝大多数业务代码不需要手动构造Future。它的典型场景是:桥接同步回调(如socket事件、C扩展通知)、实现自定义awaitable、或在低层协议栈中做状态传递。用错的信号很明确——你发现自己在写loop.create_future()后又立刻await它,或者试图用set_result()模拟协程返回。
立即学习“Python免费学习笔记(深入)”;
- 需要从非协程函数(比如signal handler、threading.Timer回调)通知异步代码时,用
Future+call_soon_threadsafe() - 写自定义异步原语(如
AsyncEvent、AsyncLock)内部状态管理时,用Future作等待句柄 - 把同步库包装成异步接口(如
concurrent.futures.ThreadPoolExecutor.submit().result()转为awaitable)时,用Future中转
真正容易被忽略的是:Future没有取消传播机制。如果你手动set_result()前,它已被cancel(),那set_result()会静默失败(不报错但也不生效),此时应先检查fut.cancelled()。










