10 | Tornado源码分析:Gen 对象(上)

2020-09-01 15:10:31 浏览数 (1)

hello 大家好 本期我们来聊聊 Tornado 之 gen 模块,这个模块在 Tornado 中的作用是实现 协程功能的。就这一模块我们打算分两期进行说明包括创建协程和运行协程。

本期我们的重点是说明一下 协程的创建。话不多说我们先看一下源码(我已经进行过整理的源码,主要方便大家去理解里面的实现逻辑,若想看完整的源码建议大家可以自行查看本机安装的 tornado 版本中的源代码)

代码语言:javascript复制
# -*- encoding: utf-8 -*-
# !/usr/bin/python
"""
@File    : gen_ll.py
@Time    : 2020/08/29 15:10
@Author  : haishiniu
@Software: PyCharm
"""
# gen 模块是 "协程" 的实现。

def coroutine(func, replace_callback=True):
    return _make_coroutine_wrapper(func, replace_callback=True)

# 1:创建协程
def _make_coroutine_wrapper(func, replace_callback):
    wrapped = func
    if hasattr(types, 'coroutine'):
        func = types.coroutine(func)

    # wrapper 表示代表一个协程对象;启动协程,会返回一个Future对象。
    @functools.wraps(wrapped)
    def wrapper(*args, **kwargs):
        # 生成一个绑定到该协程的Future对象
        future = TracebackFuture()
        if replace_callback and 'callback' in kwargs:
            # 当 replace_callback为True,并且通过关键字参数callback指定了回调函数,那么当Future对象完成时,它会将回调函数
            # lambda future: callback(future.result())
            # 添加到当前线程的IOLoop上,在下一次事件循环时,
            # 就会执行这个回调函数
            callback = kwargs.pop('callback')
            IOLoop.current().add_future(future, lambda future: callback(future.result()))

        try:
            # 调用被封装的函数(重点)
            result = func(*args, **kwargs)
        except (Return, StopIteration) as e:
            # 若抛出了gen.Return或StopIteration异常,
            # 那么则从异常对象中提取执行结果,并将结果保存到Future对象,协程执行结束。
            result = _value_from_stopiteration(e)
        except Exception:
            # 若出现其他异常,那么将异常信息保存到Future对象,协程执行结束。
            future.set_exc_info(sys.exc_info())
            return future
        else:
            if isinstance(result, types.GeneratorType):
                # 若被封装的函数是一个生成器函数,那么调用它会返回一个生成器,接下来,会启动生成器。
                # 之所以在这里启动生成器是因为很多生成器协程并不会真正的yield,也就说,在执行过程中,所经历的分支上,并没有yield语句,所以直接在这里进行迭代,以避免创建Runner对象,提升性能。
                try:
                    orig_stack_contexts = stack_context._state.contexts
                    yielded = next(result)
                    if stack_context._state.contexts is not orig_stack_contexts:
                        yielded = TracebackFuture()
                        yielded.set_exception(
                            stack_context.StackContextInconsistentError(
                                'stack_context inconsistency (probably caused '
                                'by yield within a "with StackContext" block)'))
                except (StopIteration, Return) as e:
                    # 若抛出了gen.Return或StopIteration异常,那么则从异常对象中提取执行结果,并将结果保存到Future对象,协程执行结束。
                    future.set_result(_value_from_stopiteration(e))
                except Exception:
                    # 若出现其他异常,那么,将异常信息保存到Future对象,协程执行结束。
                    future.set_exc_info(sys.exc_info())
                else:
                    # 否则,使用 生成器、代表协程执行结果的Future对象、以及生成器第一次yield的值创建Runner对象
                    _futures_to_runners[future] = Runner(result, future, yielded)
                yielded = None

                try:
                    return future
                finally:
                    future = None
        future.set_result(result)
        return future

    # 将被封装的函数保存到协程对象的属性中(编程技巧)
    wrapper.__wrapped__ = wrapped
    # 给协程对象设置一个标记,拥有该标记的对象就是一个tornado协程对象
    wrapper.__tornado_coroutine__ = True
    # 返回协程对象
    return wrapper

好了,到此使用Gen模块来创建协程的部分结束了,我们简单的小结一下:gen.coroutine修饰的函数,就是一个“协程”,调用(或者叫启动)一个协程,会返回一个Future对象。 本期就先分享到这里,有任何问题欢迎后台留言交流,下期我们再来聊聊如何启动运行一个协程以及什么情况下会终止一个协程。

0 人点赞