Skip to content

实现 Future 和 Task

这一章写 FutureTask。先看分工:Future 保存结果;Task 拿着 coroutine,每次被叫醒就把 coroutine 往下推进一步。

Future

Future 里面存几样东西:

python
self._done = False
self._result = None
self._exception = None
self._callbacks = []

完成时:

python
def _finish(self):
    self._done = True
    callbacks = list(self._callbacks)
    self._callbacks.clear()
    for callback in callbacks:
        self._loop.call_soon(callback, self)

这里没有直接调用 callback,代码会把 callback 交给事件循环。Future 完成时,常常会唤醒 Task;如果在 Future 完成的地方直接恢复 Task,执行顺序会变得难追。统一放进 ready 队列,后面排查问题会简单很多。

Task

Task 最后也会有结果或异常,所以它可以像 Future 一样被 await。但 Task 多拿着一个 coroutine:

python
self._coro = coro
self._loop.call_soon(self._step)

创建 Task 时,它只把 _step 放进 ready 队列,不会在这一行直接运行 coroutine。这样 create_task() 很快返回,真正执行交给事件循环。

_step 的三种结局

async def 里的 return value 运行时会变成 StopIteration.value。Task 捕获到它,就把自己标记为完成。

_wakeup

python
def _wakeup(self, future):
    self._waiting_on = None
    self._step(future)

Future 完成后,Task 从 future 里取结果,再把这个结果送回 coroutine:

python
yielded = self._coro.send(future.result())

如果 future.result() 抛异常,这个异常会沿着 Task 继续往外走。标准库在这里处理了很多特殊情况,教学版只保留这一条主路。

取消

教学版 Task.cancel()

python
self._must_cancel = True
if self._waiting_on is not None:
    self._waiting_on.cancel()
self._loop.call_soon(self._step)

下一次 _step 会:

python
yielded = self._coro.throw(CancelledError())

这和标准库的基本做法一致:取消会往协程里抛一个异常,让它有机会清理。

小练习

运行:

bash
python3 -m unittest discover -s examples/mini_asyncio_runtime/tests

然后打开测试里的 test_task_cancel_injects_cancelled_error,把 child 中的 except aio.CancelledError 删除。这个测试会失败,因为 child 不再把取消转换成 "cancelled" 这个返回值。

面向学习目的的 Python asyncio 中文教程与 mini_asyncio 教学运行时。