美文网首页
tornado协程调度

tornado协程调度

作者: EamonYi | 来源:发表于2019-10-04 12:27 被阅读0次

    环境配置

    win10
    pycharm64 5.0.3
    python 3.6.7
    tornado 5.1.1

    示例代码

    参考自tornado协程(coroutine)原理

    from tornado.gen import coroutine
    
    @coroutine
    def asyn_sum(a, b):
        print("begin calculate:sum %d+%d"%(a,b))
        result = yield simple_plus(a, b)
    
        print("after yielded")
        print("the %d+%d=%d" % (a, b, result))
    
    @coroutine
    def simple_plus(a, b):
        return a+b
    
    def main():
        asyn_sum(2, 3)
        # tornado.ioloop.IOLoop.instance().start()
    
    if __name__ == "__main__":
        main()
    

    执行过程

    @coroutine装饰的方法实际执行的函数是gen.py中的_make_coroutine_wrapper装饰器返回的方法wrapper(暂且称之为asyn_sum_wrapper),且wrapper中的functypes.coroutine处理后的方法。

    asyn_sum执行过程

    asyn_sum_wrapper方法的执行结果result = func(*args, **kwargs)实际是一个GeneratorType类型的对象,实际的执行是在yielded = next(result)中。执行到result = yield simple_plus(a, b)时,进入simple_plus被装饰后的方法wrapper中(暂且称之为simple_plus_wrapper)。simple_plus_wrapper返回一个Future对象,其result属性为5,即其实际返回值,随后跳转到runner = Runner(result, future, yielded)

    Runner的实例化有一段执行代码

    if self.handle_yield(first_yielded):
        gen = result_future = first_yielded = None
        self.run()
    

    self.handle_yield会将self.future置为first_yielded,然后各个指向这个future的变量置空;在self.run()self.future会赋值给局部变量future,然后被置空;在执行value = future.result()后该局部变量也被置空。future最终只有外层的Runner初始化时候的引用,Runner返回后yielded=None,等待释放。

    获得了simple_plus返回结果后,gen.py使用yielded = self.gen.send(value)回归到asyn_sum的yield,并将返回值写入result,此时self.run()被挂起,等asyn_sum执行完后恢复执行。

    asyn_sum执行完后,会跳转到异常捕获逻辑except (StopIteration, Return) as e:e就是asyn_sum的返回值,后续被future_set_result_unless_cancelled(self.result_future, _value_from_stopiteration(e))将值写回到result_future中,即asyn_sum_wrapper调用初期实例化的future对象。

    simple_plus执行过程

    simple_plus同样也是在simple_plus_wrapperresult = func(*args, **kwargs)处执行,不同的是,simple_plus方法体不包含yield,所以在types.coroutine.wrapped返回的不再是GeneratorType类型的对象,而是simple_plus方法的实际执行结果5,然后在装饰器内会包装上Future返回给上层调用者。

    future

    @coroutine装饰器的wrapper中,第一步就是定义一个future对象,在asyn_sum_wrapper执行过程中,由于内部调用了yield,所以在它的future被使用前,又进入了simple_plus_wrapper。由于后者的result是非GeneratorType类型,所以跳转到了future_set_result_unless_cancelled(future, result)

    从gen.py中可以追溯Future的定义位置,根据实际单步定位,Future定义在_asyncio.py中,因为future的方法无法跳转。

    # gen.py
    from tornado.concurrent import (Future, is_future, chain_future, future_set_exc_info,
                                    future_add_done_callback, future_set_result_unless_cancelled)
    
    # concurrent.py
    if asyncio is not None:
        Future = asyncio.Future  # noqa
    
    if futures is None:
        FUTURES = Future  # type: typing.Union[type, typing.Tuple[type, ...]]
    else:
        FUTURES = (futures.Future, Future)
    

    协程切换

    参考自关于协程的 send(None)
    为方便对yielded = self.gen.send(value)的理解,此处保留代码示例。

    def consumer():
        r = ''
        while True:
            n = yield r
            if not n:
                return
            print('[CONSUMER] Consuming %s...' % n)
            r = '200 OK'
    
    def produce(c):
        c.send(None)
        n = 0
        while n < 5:
            n = n + 1
            print('[PRODUCER] Producing %s...' % n)
            r = c.send(n)
            print('[PRODUCER] Consumer return: %s' % r)
        c.close()
    
    c = consumer()
    produce(c)
    

    未了解部分

    Runner中的self._deactivate_stack_context()future_set_exc_info(self.result_future, sys.exc_info())

    小结

    tornado中每一个被coroutine装饰的方法都会对应一个future,根据调用的深度,future的嵌套深度逐渐增加,直到某一个future set_result(result = func(*args, **kwargs)返回结果为非生成器类型),然后进行后续future的嵌套处理。

    对yield AsyncHTTPClient的调用,yielded = next(result)会获取到fetch返回的future对象,与调用者及其future对象一并生成一个Runner对象,供IOloop异步回调,runner中yielded = self.gen.send(value)会触发协程从断点继续执行。

    相关文章

      网友评论

          本文标题:tornado协程调度

          本文链接:https://www.haomeiwen.com/subject/fofeaqtx.html