AirJD 焦点
AirJD

没有录音文件
00:00/00:00
加收藏

探索Python 3.5中async-await特性的实现 by 赖勇浩

发布者 pyconf   简介 PyChina 大会
发布于 1460336746083  浏览 15377 关键词 Python, 性能 
分享到

第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



支持文件格式:*.pdf
上传最后阶段需要进行在线转换,可能需要1~2分钟,请耐心等待。