说完了asyncio
事件循环是如何运行异步任务的,接下来back to basic,我们一起看看async
和await
两个原语具体代表了什么含义。
首先是async
,async
通常用来修饰一个函数,表示这个函数会返回一个协程。比如说:
1 | async def _coro_maker(i): |
对_coro_maker
进行反编译,得到这样的结果:
1 | Disassembly of _coro_maker: |
可以看到,函数体内反编译的结果和普通def函数是一致的,唯一的不同是最开始加了GEN_START
字节码。首先看GEN_START
的实现。
1 | case TARGET(GEN_START): { |
GEN_START
对于生成器、协程、async
生成器都适用。对于async def
函数其真实含义是调用send(None)
以触发协程的开始。由于async def
生成的协程没有yield
值,因此会报StopIteration
异常并给出协程的返回值。
总的来说,协程是一种特殊的generator
,具备被await
的效果。
那么接下来,该说await
了。我们看一组代码示例:
1 | async def _coro_maker(i): |
反编译_test_await
的结果,得到:
1 | Disassembly of _test_await: |
首先来看GET_AWAITABLE
,其代码实现如下:
1 | case TARGET(GET_AWAITABLE): { |
GET_AWAITABLE
做了这样几件事情:
- 通过
_PyCoro_GetAwaitableIter
获取一个Awaitable
对象的迭代器iter
- 检查
iter
是否合法,检查当前Awaitable
对象是否已经被await
了 - 将
iter
置于栈顶
Awaitable
的iter
到底是什么东西?我们来看_PyCoro_GetAwaitableIter
的实现:
1 | PyObject * |
其中有重要的几句代码:
if (PyCoro_CheckExact(o) || gen_is_coroutine(o)) return o
getter = ot->tp_as_async->am_await
PyObject *res = (*getter)(o)
可以知晓,如果对象是协程的话会直接返回,不是协程的话看有无ot->tp_as_async->am_await
接口支持。如果再追究的话,对于一般的生成器PyGen_Type
,是没有这个接口的,所以是无法被await
的。
GET_AWAITABLE
之后,接下来是load
了一个None
,然后YIELD_FROM
。YIELD_FROM
实现如下:
1 | case TARGET(YIELD_FROM): { |
通过YIELD_FROM
操作,实际上调用了PyIter_Send(coro, None, &retval)
。我们来看PyIter_Send
的实现:
1 | PySendResult |
针对AwaitableIter
,实际调用了Py_TYPE(iter)->tp_as_async->am_send(iter, arg, result)
,对应的函数是这个:
1 | static PySendResult |
看到这里就很令人熟悉了,没错,gen_send_ex2(coro, None, result, 0, 0)
就是coro.send(None)
的逻辑
在gen_send_ex2
中,函数体的返回值会正好赋到result
上。再看YIELD_FROM
里if (gen_status == PYGEN_RETURN)
分支,最终的返回值就会放到栈顶。
当我们调用xx = await Awaitable
时,我们也就能够把Awaitable
的返回值赋给xx
了。这样,await
原语就实现了它的作用。