第1页
探索Python 3.5中async/await特 性的实现
广州齐昌网络科技有限公司 赖勇浩
第2页
2015-6-2
第3页
2015-6-2
第4页
自我介绍
第5页
赖勇浩
http://laiyonghao.com
第6页
创业者,程序员,社区控
2014 -
2005 -
2009 -
第7页
合著有《编写高质量代码:改善Python程序的91个建议》
第8页
2009年
第9页
2015年7月
第10页
背景知识
10 10
第11页
Python C语言 虚拟机实现 协程概念
第12页
Python协程的演化
12 12
第13页
• Python 2.2, 2001.12
– PEP 255 - Simple Generators
第14页
def generate_ints(N): for i in range(N): yield i
14 14
第15页
• Python 2.5 2006.8
– PEP 342 -- Coroutines via Enhanced Generators – In 2.5, yield is now an expression – Add send()/throw()/close()
第16页
• Python 3.3 2012.9
– PEP 0380 -- Syntax for Delegating to a Subgenerator
16 16
第17页
>>> def g(x): ... yield from range(x, 0, -1) ... yield from range(x) ... >>> list(g(5)) [5, 4, 3, 2, 1, 0, 1, 2, 3, 4]
17 17
第18页
• Python 3.5 2015.9
– PEP 0492 -- Coroutines with async and await syntax
第19页
async def read_data(db): data = await db.fetch('SELECT …') …
19 19
第20页
async/await的意义
• 定义了原生协程,与生成器彻底区分开来 • 解决with/for的异步需求。
第21页
async with
async with EXPR as VAR: BLOCK
第22页
async with
• 同步的 __enter__/__exit__
– 无法实现获取、释放资源时复杂耗时的操作异 步
• 异步的 __aenter__/__aexit__
第23页
async with
class AsyncContextManager: async def __aenter__(self): await log('entering context') async def __aexit__(self, exc_type, exc, tb): await log('exiting context')
23 23
第24页
async for
async for TARGET in ITER: BLOCK
else: BLOCK2
第25页
async for
• 同步的 __iter__/__next__
– 无法实现获取、释放资源时复杂耗时的操作异步
• 异步的 __aiter__/__anext__
第26页
async for
class AsyncIterable: async def __aiter__(self): return self async def __anext__(self): data = await self.fetch_data() if data: return data else: raise StopAsyncIteration async def fetch_data(self): ...
第27页
体验Python协程
27 27
第28页
pyenv install 3.5.0
第29页
神器! https://github.com/yyuu/pyenv
第30页
mkdir py35lab cd py35lab pyenv local 3.5.0
第31页
python –version Python 3.5.0
31 31
第32页
探索async/await的实现
32 32
第33页
async/await的字节码
async def foo(): return 42
async def bar(): print(await foo())
import dis dis.dis(bar)
第34页
async/await的字节码
9 0 LOAD_GLOBAL
0 (print)
3 LOAD_GLOBAL
1 (foo)
6 CALL_FUNCTION
0 (0 positional, 0 keyword pair)
9 GET_AWAITABLE
10 LOAD_CONST
0 (None)
13 YIELD_FROM
14 CALL_FUNCTION
1 (1 positional, 0 keyword pair)
17 POP_TOP
18 LOAD_CONST
0 (None)
21 RETURN_VALUE
第35页
GET_AWAITABLE
第36页
_PyCoro_GetAwaitableIter
第37页
_PyCoro_GetAwaitableIter
* This helper function returns an awaitable for `o`: * - `o` if `o` is a coroutine-object; * - `type(o)->tp_as_async->am_await(o)`
第38页
am_await
static PyObject * coro_await(PyCoroObject *coro) {
PyCoroWrapper *cw = ...New(PyCoroWrapper, &_PyCoroWrapper_Type);
cw->cw_coroutine = coro; return (PyObject *)cw; }
第39页
_PyCoroWrapper_Type
PyTypeObject _PyCoroWrapper_Type = {
...
PyObject_SelfIter,
/* tp_iter */
(iternextfunc)coro_wrapper_iternext, /* tp_iternext */
coro_wrapper_methods,
/* tp_methods */
...
};
第40页
GET_AWAITABLE让coroutine的 返回值(Awaitable)入栈
第41页
async/await的字节码
9 0 LOAD_GLOBAL
0 (print)
3 LOAD_GLOBAL
1 (foo)
6 CALL_FUNCTION
0 (0 positional, 0 keyword pair)
9 GET_AWAITABLE
10 LOAD_CONST
0 (None)
13 YIELD_FROM
14 CALL_FUNCTION
1 (1 positional, 0 keyword pair)
17 POP_TOP
18 LOAD_CONST
0 (None)
21 RETURN_VALUE
41 41
第42页
YIELD_FROM
第43页
YIELD_FROM
• 获取栈顶元素, v • 调用_PyGen_Send(..., v),并返回结果。
第44页
async/await真相
• Native Corotine 就是换了马甲的 generator
第45页
PythonVM中的协程
Corotine typedef struct {
_PyGenObject_HEAD(cr) } PyCoroObject;
Generator typedef struct { _PyGenObject_HEAD(gi) } PyGenObject;
第46页
_PyGenObject_HEAD
#define _PyGenObject_HEAD(prefix) \
PyObject_HEAD
\
struct _frame *prefix##_frame; \
char prefix##_running;
\
PyObject *prefix##_code;
\
PyObject *prefix##_weakreflist; \
PyObject *prefix##_name;
\
PyObject *prefix##_qualname;
第47页
协程从何处来?
• 代码编译、执行,就是一个 PyCodeObject* co • co->co_flag 标识了类型 • Py3.5: CO_COROUTINE, CO_ITERABLE_COROUTINE • async def 使 co_flag 具有 CO_COROTINE
if (is_coro) { gen = PyCoro_New(f, name, qualname);
}
第48页
协程的运行与终止
• 解释器遇到 YIELD_FROM,调用 _PyGen_Send,主要逻辑在 gen_send_ex
第49页
gen_send_ex
• 做好参数、状态的检查工作 • 参数压栈 • 保存要返回栈帧(PyFrameObject) • 设置运行状态标志 • 调用 PyEval_EvalFrameEx 从自己的栈帧执行
代码 • 重置运行状态标志 • 恢复现场,异常处理,释放资源,返回结
果
第50页
Q&A
50 50