Tornado协程原理 - Go语言中文社区

Tornado协程原理


协程的定义

  • 函数
    入口:有且只有一个入口
    出口:有且只有一个出口

  • 协程
    入口:多个入口
    出口:多个出口
    特点:暂定,保留执行状态,恢复执行

协程是函数基础上,一种更加宽泛定义的计算机程序模块,它可以有多个入口点,允许从一个入口点到下个入口点之前暂停保留执行状态,等待合适的时机恢复执行状态,从下一个入口点重新开始性质

  • 协程代码块
    从一个入口点到下一个入口点的代码

  • 协程模块
    有n个入口代码,和n个协程代码块组成。其组织形式为:函数入口-》协程代码块-》入口点-》协程代码块...

可迭代对象,迭代器,生成器

可迭代对象(Iterable)

python中可迭代类型如字符串,列表,元组,字典等:

str = 'abc'
list = ['a', 'b', 'c']
set = ('a', 'b', 'c')
dict = {'a':1, 'b':2}

他们的特点是dir(object)实现了__iter__()函数

迭代器(Iterator)

迭代器的要求是dir(object)实现了__iter__()__next__()两个方法

生成器(generator)

生成器是特殊的迭代器,在迭代器的基础上包含yield关键字

总结

  • 迭代器必须实现迭代器协议__iter____next__
  • __iter__返回的对象是可迭代对象
  • 迭代器一定是可迭代对象,但可迭代对象不一定是迭代器,有可能迭代细节交给另一个类,这个类才是迭代器
  • 生成器一定是一个迭代器,同时也是迭代对象
  • 生成器是一种特殊的迭代器,包含关键字yield来实现懒惰计算,并是的外部影响生成器的执行成为可能

生成器和yield语义

生成器的定义:提供一种函数,能够返回中间结果给调用者,然后维护函数的局部状态,以便当函数离开后,也能恢复执行。
生成器是一个包含关键字yield表达式的函数。一个生成器是异步的,即生成器模块中含有阻塞代码,生成器的类型是types.GeneratorType

#生成器
def sum(total):
    total = 0
    while True:
        a = yield total
        total += a

if __name__ == '__main__':
    s = sum(10)      //获得生成器,但不执行生成器的代码
    print next(s)               //运行生成器的代码,执行yield total处,输出0
    print s.send(10)         //运行生成器的代码,第二次执行到yield total处,输出10
    print s.send(2)           //运行生成器的代码,第三次执行到yield total处,输出12

对上诉代码进行说明:

  • sum是一个生成器,包含关键字yield
  • sum生成器 a=yield从send(value)获取外部参数
  • sum生成器 yield total将total值传出
  • next(s)初始化生成器,并block在第一个yield中
  • s.send(v)唤醒生成器,并将值传入

Generator已经具备协程的能力,如能够暂停,保存状态,传出值;恢复执行,接受参数,异步执行。
但此时Generator还不是一个协程。一个真正的协程能够控制代码什么时候继续执行。而此时的Generator执行遇到一个yield还是把执行控制权转移给调用者

Future

Future顾名思义未来,将在未来被执行的代码。Future的类包含result,done, callback属性,以及_set_result, _set_done方法,通过这些属性和方法来实现当future被赋值后,能够执行对应的callback函数,具体代码如下:

class Future(object):
    def done(self):
        return self._done

    def result(self, timeout=None):
        self._clear_tb_log()
        if self._result is not None:
            return self._result
        if self._exc_info is not None:
            raise_exc_info(self._exc_info)
        self._check_done()
        return self._result

    def add_done_callback(self, fn):
        if self._done:
            fn(self)
        else:
            self._callbacks.append(fn)

    def set_result(self, result):
        self._result = result
        self._set_done()

    def _set_done(self):
        self._done = True
        for cb in self._callbacks:
            try:
                cb(self)
            except Exception:
                app_log.exception('exception calling callback %r for %r',
                                  cb, self)
        self._callbacks = None

Future说明
1.Future对象通过add_done_callback将回调函数与future对象进行绑定,目的是future被赋值后调用callback中的回调函数,从而实现激活生成器(回调函数调用gen.send(value)
2.def set_result(self, result):对Future对象赋值,将异常处理结果赋值给Future对象的result属性,同时执行_set_done遍历callback的函数
3.def _set_done(self):遍历callback的函数列表,并执行

总结来说,future对象绑定一个回调函数(回到函数会激活生成器),当future对象被赋值,会回调回调函数,进而回调函数会激活生成器(gen.send())

IOLoop类

IOLoop在协程运行环境中担任着协程调度的角色,消息循环本质上是一种事件循环,等待事件,然后运行对应事件的处理器。IOLoop主要调度处理的是IO事件(读,写,错误)以及callback和timeout。
IOLoop中注册future:add_future

def add_future(self, future, callback):
    assert is_future(future)
    callback = stack_context.wrap(callback)
    future.add_done_callback(lambda future: self.add_callback(callback, future))

IOLoop将callback和future作为参数传入IOLoop的add_callback,封装成匿名函数,并存储到future的callback属性中,当future被设置后匿名函数则会在下一个IOLoop中运行

  • 1.lambda future: self.add_callback(callback, future),将callback,future作为参数传入IOLoop的add_callback方法,实则利用偏函数将callback和future进行封包装,包装好的偏函数加入到IOLoop的回调函数列表中。当IOLoop下一次迭代运行,遍历回调函数并执行
    1. 匿名函数加入到future的callback属性中,当future对象被设置后,会调用回调函数

Coroutine函数装饰器

函数装饰器本质是一个函数,它将被调用的函数func作为参数,返回一个新的函数make_coroutine_wrapper。

def coroutine(func, replace_callback=True):
      return make_coroutine_wrapper(func, replace_callback=True)
image.png

如上图:

    1. result = func(*arg, **kwargs)获取函数对象,
    1. if isinstance(result, types.GeneratorType)判断result是否是生成器
    1. yielded = next(result) 因为result是生成器,当调用next会执行到生成器第一个yield代码,并返回一个future对象(异步操作)赋值给yield
    1. Runner(result, future, yielded),将生成器对象, yielded(future对象),future(监控future)传入函数Runner
class Runner(object):
    def __init__(self, gen, result_future, first_yielded):
        self.gen = gen
        self.result_future = result_future
        self.future = _null_future
        self.yield_point = None
        self.pending_callbacks = None
        self.results = None
        self.running = False
        self.finished = False
        self.had_exception = False
        self.io_loop = IOLoop.current()
        self.stack_context_deactivate = None
        if self.handle_yield(first_yielded):
            self.run()

    def run(self):
        if self.running or self.finished:
            return
        try:
            self.running = True
            while True:
                future = self.future
                if not future.done():
                    return
                self.future = None
                try:
                    try:
                        value = future.result()
                    except Exception:
                        self.had_exception = True
                        yielded = self.gen.throw(*sys.exc_info())
                    else:
                        yielded = self.gen.send(value)
                except (StopIteration, Return) as e:
                    self.finished = True
                    self.future = _null_future
                    self.result_future.set_result(getattr(e, 'value', None))
                    self.result_future = None
                    return
                except Exception:
                    self.finished = True
                    self.future = _null_future
                    self.result_future.set_exc_info(sys.exc_info())
                    self.result_future = None
                    return
                if not self.handle_yield(yielded):
                    return
        finally:
            self.running = False

    def handle_yield(self, yielded):

        try:
            self.future = convert_yielded(yielded)
        except BadYieldError:
            self.future = TracebackFuture()
            self.future.set_exc_info(sys.exc_info())

        if not self.future.done() or self.future is moment:
            self.io_loop.add_future(
                self.future, lambda f: self.run())
            return False
        return True

Runner函数内部处理就是将给yeilded(future对象)绑定一个回调函数,当future对象被set_result后,调用run;而run函数内部会调用生成器的send方法,并将vaule传入到生成器。
def handle_yield(self, yielded):

  • 1.将yielded转化为future对象
  • 2.调用IOLoop的add_future函数,将run函数添加到future的callback属性中
  • 3.当future对象在某处代码中被set_result,IOLoop下一个循环中便执行run函数
  • 4.run函数会取出future的result,并调用gen.send(value)启动生成器,并将生成器的输出在赋值给yielded
  • 5.重新赋值的yielded在作为参数传入handle_yield(self, yielded),循环步骤1,指导生成器结束

参考:
https://juejin.im/post/5ccafbf5e51d453a3a0acb42
https://blog.csdn.net/wyx819/article/details/45420017

版权声明:本文来源简书,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://www.jianshu.com/p/35d046d8b8a8
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。
  • 发表于 2020-01-12 13:03:32
  • 阅读 ( 1246 )
  • 分类:

0 条评论

请先 登录 后评论

官方社群

GO教程

推荐文章

猜你喜欢