Python3,5中async await特性的实现
2020-02-27 268浏览
- 1.探索Python 3.5中async/await特 性的实现 广州齐昌网络科技有限公司 赖勇浩
- 2.2015-6-2
- 3.2015-6-2
- 4.自我介绍 4
- 5.赖勇浩http://laiyonghao.com5
- 6.创业者,程序员,社区控 2014 - 2005 - 2009 - 6
- 7.合著有《编写高质量代码:改善Python程序的91个建议》 7 7
- 8.2009年 8 8
- 9.2015年7月 9
- 10.背景知识 10 10
- 11.Python C语言 虚拟机实现 协程概念 11
- 12.Python协程的演化 12 12
- 13.• Python 2.2, 2001.12 – PEP 255 - Simple Generators 13
- 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() 15
- 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 18
- 19.async def read_data(db): data = await db.fetch('SELECT …') … 19 19
- 20.async/await的意义 • 定义了原生协程,与生成器彻底区分开来 • 解决with/for的异步需求。 20
- 21.async with async with EXPR asVAR:BLOCK 21
- 22.async with • 同步的 __enter__/__exit__ – 无法实现获取、释放资源时复杂耗时的操作异 步 • 异步的 __aenter__/__aexit__ 22
- 23.async with classAsyncContextManager: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 inITER:BLOCKelse:BLOCK2 24
- 25.async for • 同步的 __iter__/__next__ – 无法实现获取、释放资源时复杂耗时的操作异步 • 异步的 __aiter__/__anext__ 25
- 26.async for classAsyncIterable:async def __aiter__(self): return self async def __anext__(self): data = await self.fetch_data() ifdata:return dataelse:raise StopAsyncIteration async def fetch_data(self): ... 26
- 27.体验Python协程 27 27
- 28.pyenv install 3.5.0 28
- 29.神器!https://github.com/yyuu/pyenv29
- 30.mkdir py35lab cd py35lab pyenv local 3.5.0 30
- 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) 33
- 34.async/await的字节码 9 0 LOAD_GLOBAL 3 LOAD_GLOBAL 6 CALL_FUNCTION 9 GET_AWAITABLE 10 LOAD_CONST 13 YIELD_FROM 14 CALL_FUNCTION 17 POP_TOP 18 LOAD_CONST 21 RETURN_VALUE 0 (print) 1 (foo) 0 (0 positional, 0 keyword pair) 0 (None) 1 (1 positional, 0 keyword pair) 0 (None) 34
- 35.GET_AWAITABLE 35
- 36._PyCoro_GetAwaitableIter 36
- 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)` 37
- 38.am_await static PyObject * coro_await(PyCoroObject *coro) { PyCoroWrapper *cw = ...New(PyCoroWrapper, &_PyCoroWrapper_Type); cw->cw_coroutine = coro; return (PyObject *)cw; } 38
- 39._PyCoroWrapper_Type PyTypeObject _PyCoroWrapper_Type = { ... PyObject_SelfIter, /* tp_iter */ (iternextfunc)coro_wrapper_iternext, /* tp_iternext */ coro_wrapper_methods, /* tp_methods */ ... }; 39
- 40.GET_AWAITABLE让coroutine的 返回值(Awaitable)入栈 40
- 41.async/await的字节码 9 0 LOAD_GLOBAL 3 LOAD_GLOBAL 6 CALL_FUNCTION 9 GET_AWAITABLE 10 LOAD_CONST 13 YIELD_FROM 14 CALL_FUNCTION 17 POP_TOP 18 LOAD_CONST 21 RETURN_VALUE 0 (print) 1 (foo) 0 (0 positional, 0 keyword pair) 0 (None) 1 (1 positional, 0 keyword pair) 0 (None) 41 41
- 42.YIELD_FROM 42
- 43.YIELD_FROM • 获取栈顶元素, v • 调用_PyGen_Send(..., v),并返回结果。 43
- 44.async/await真相 • Native Corotine 就是换了马甲的 generator 44
- 45.PythonVM中的协程 Corotine Generator typedef struct { _PyGenObject_HEAD(cr) } PyCoroObject; typedef struct { _PyGenObject_HEAD(gi) } PyGenObject; 45
- 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; 46
- 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); } 47
- 48.协程的运行与终止 • 解释器遇到 YIELD_FROM,调用 _PyGen_Send,主要逻辑在 gen_send_ex 48
- 49.gen_send_ex • • • • • 做好参数、状态的检查工作 参数压栈 保存要返回栈帧(PyFrameObject) 设置运行状态标志 调用 PyEval_EvalFrameEx 从自己的栈帧执行 代码 • 重置运行状态标志 • 恢复现场,异常处理,释放资源,返回结 果 49
- 50.Q&A 50 50