tornado 源码之 StackContext(一)
tornado 的异步上下文机制分析
contents
我们实现一个简单的 MyIOLoop 类,模仿 tornado 的 IOLoop,实现异步回调
实现一个简单的 MyStackContext 类,模仿 tornado 的 StackContext,实现上下文
MyIOLoop
模拟 tornado IOLoop
1 | class MyIOLoop: |
异步回调异常的捕获
由输出可以看到,回调函数 call_func 中抛出的异常,在 main 函数中无法被捕获
main 函数只能捕获当时运行的 async_task 中抛出的异常, async_task 只是向 MyIOLoop 注册了一个回调,并没有当场调用回调
call_func 函数最终在 MyIOLoop.start 中调用,其异常没有被捕获
1 | my_io_loop = MyIOLoop.instance() |
使用 wrap
可以使用 wrap 的方式,把函数调用和异常捕捉写在一起,回调实际调用的是带异常捕捉的函数 wrapper
1 | my_io_loop = MyIOLoop.instance() |
由此,可以想到,构造一个上下文环境,使用全局变量保存这个执行环境,等回调函数执行的时候,构造出这个环境
使用 contextlib
下面模仿了 tornado 异步上下文实现机制
- MyStackContext 使用 __enter__ __exit__ 支持上下文
- MyStackContext 构造函数参数为一个上下文对象
- with MyStackContext(context) 进行如下动作:
在 MyStackContext(context) 构造时,把 context 注册进全局工厂 MyStackContext.context_factory- 进入 MyStackContext 的__enter
- 构造一个 context 对象
- 调用 context 对象的 __enter,进入真正 context 上下文
- 执行 context 上下文,my_context yield 语句前的部分
- 执行上下文包裹的语句,async_task
- async_task 中 add_callback,实际保存的 wrap, wrap 将此时的全局上下文环境 MyStackContext.context_factory 保存,以方便 call_back 调用
- 调用 context 对象的 __exit, 退出 context 上下文
- 进入 MyStackContext 的__exit
- my_io_loop.start() 执行, 调用注册的 _call_back
- 实际调用 wrapped 函数
- 获取保存的 context 环境
- with context
- 调用真正的 callback
这样,在 main 函数中执行
1 | with MyStackContext(my_context): |
构造一个执行上下文 my_context,异步函数将在这个上下文中调用
效果上相当于在 my_context 这个上下文环境中调用 async_task
类似:
1 | def my_context(): |
1 | import contextlib |
inspired by
Tornado 源码分析(二)异步上下文管理(StackContext)
copyright
author:bigfish
copyright: 许可协议 知识共享署名 - 非商业性使用 4.0 国际许可协议
Sync From: https://github.com/TheBigFish/blog/issues/9