实现 Future 和 Task
这一章写 Future 和 Task。先看分工:Future 保存结果;Task 拿着 coroutine,每次被叫醒就把 coroutine 往下推进一步。
Future
Future 里面存几样东西:
self._done = False
self._result = None
self._exception = None
self._callbacks = []完成时:
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:
self._coro = coro
self._loop.call_soon(self._step)创建 Task 时,它只把 _step 放进 ready 队列,不会在这一行直接运行 coroutine。这样 create_task() 很快返回,真正执行交给事件循环。
_step 的三种结局
async def 里的 return value 运行时会变成 StopIteration.value。Task 捕获到它,就把自己标记为完成。
_wakeup
def _wakeup(self, future):
self._waiting_on = None
self._step(future)Future 完成后,Task 从 future 里取结果,再把这个结果送回 coroutine:
yielded = self._coro.send(future.result())如果 future.result() 抛异常,这个异常会沿着 Task 继续往外走。标准库在这里处理了很多特殊情况,教学版只保留这一条主路。
取消
教学版 Task.cancel():
self._must_cancel = True
if self._waiting_on is not None:
self._waiting_on.cancel()
self._loop.call_soon(self._step)下一次 _step 会:
yielded = self._coro.throw(CancelledError())这和标准库的基本做法一致:取消会往协程里抛一个异常,让它有机会清理。
小练习
运行:
python3 -m unittest discover -s examples/mini_asyncio_runtime/tests然后打开测试里的 test_task_cancel_injects_cancelled_error,把 child 中的 except aio.CancelledError 删除。这个测试会失败,因为 child 不再把取消转换成 "cancelled" 这个返回值。