tornado.gen.coroutine的作用

tornado.gen.coroutine-协程

  1. yield的基本概念

  2. Future的用法

  3. ioloop的常用接口

  4. gen.coroutine的应用

  5. gen.coroutine的源码

  6. Runner类的实现

  7. yield的基本概念
    python中使用yield实现了生成器函数,同样yield.send( )方法也实现了控制权在函数间的跳转。
    关于yield比较详细的介绍,可以参考这篇博文
    http://www.cnblogs.com/coderzh/articles/1202040.html

2.Future的用法
Future是用来在异步过程中,存放结果的容器。它可以在结果出来时,进行回调。
add_done_callback(self, fn): 添加回调函数fn,函数fn必须以future作为参数。
set_result(self, result): 存放结果,此函数会执行回调函数。
set_exception(self, exception):存放异常,此函数会执行回调函数。

  1. ioloop的常用接口
    add_future(self, future, callback): 当future返回结果时,会将callback登记到ioloop中,由ioloop在下次轮询时调用。
    add_callback(self, callback, *args, **kwargs): 将callback登记到ioloop中,由ioloop在下次轮询时调用。
    add_timeout(self, deadline, callback, *args, **kwargs): 将callback登记到ioloop中,由ioloop在指定的deadline时间调用。
    def call_at(self, when, callback, *args, **kwargs): 将callback登记到ioloop中,由ioloop在指定的when时间调用。
    def call_later(self, delay, callback, *args, **kwargs): 将callback登记到ioloop中, 由ioloop在指定的延迟delay秒后调用。
    add_handler(self, fd, handler, events): 将文件符fd的event事件和回调函数handler登记到ioloop中,当事件发生时,触发。

  2. gen.coroutine的应用
    直接上官网的例子
    普通的回调函数的方式:
    class AsyncHandler(RequestHandler):
    @asynchronous
    def get(self):
    http_client = AsyncHTTPClient()
    http_client.fetch(“http://example.com”,
    callback=self.on_fetch)
    def on_fetch(self, response):
    do_something_with_response(response)
    self.render(“template.html”)
    同步的方式:
    class GenAsyncHandler(RequestHandler):
    @gen.coroutine
    def get(self):
    http_client = AsyncHTTPClient()
    response = yield http_client.fetch(“http://example.com”)
    do_something_with_response(response)
    self.render(“template.html”)
    可以看出代码明显清晰,简单多了。如果有深层次的回调,效果会更明显。

  3. gen.coroutine的源码
    def coroutine(func, replace_callback=True):
    return _make_coroutine_wrapper(func, replace_callback=True)
    接着看_make_coroutine_wrapper的源码:
    def _make_coroutine_wrapper(func, replace_callback):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
    future = TracebackFuture()
    if replace_callback and ‘callback’ in kwargs:
    callback = kwargs.pop(‘callback’)
    IOLoop.current().add_future(
    future, lambda future: callback(future.result()))
    try:
    result = func(*args, **kwargs)
    except (Return, StopIteration) as e:
    result = getattr(e, ‘value’, None)
    except Exception:
    future.set_exc_info(sys.exc_info())
    return future
    else:
    if isinstance(result, types.GeneratorType):
    # Inline the first iteration of Runner.run. This lets us
    # avoid the cost of creating a Runner when the coroutine
    # never actually yields, which in turn allows us to
    # use “optional” coroutines in critical path code without
    # performance penalty for the synchronous case.
    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:
    future.set_result(getattr(e, ‘value’, None))
    except Exception:
    future.set_exc_info(sys.exc_info())
    else:
    Runner(result, future, yielded)
    try:
    return future
    finally:
    future = None
    future.set_result(result)
    return future
    return wrapper

这段代码注意到,有几个关键地方。
future = TracebackFuture()
TracebackFuture就是Future,两者是同一个类。这里定义了future变量, 是用来后面增加callback用的。当函数执行完时,会将这个future返回。
result = func(*args, **kwargs)
这里执行被装饰的函数,返回result。我们知道有yield语句的函数,返回结果是一个生成器。
yielded = next(result)
返回yield结果, next(result)被要求返回Future,list, dict,或者YieldPoint类型,一般返回Future。
Runner(result, future, yielded)
实例化Runner,这步很重要。
return future
返回future。

  1. 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()
    __init__构造函数首先初始化参数,然后调用handle_yield方法。

因为first_yielded为Future类型, 所以简化下handle_yield的代码。
def handle_yield(self, yielded):
self.future = yielded
if not self.future.done():
self.io_loop.add_future(
self.future, lambda f: self.run())
return False
return True
handle_yield方法,首先判断yielded是否已经返回结果,如果没有就调用io_loop.add_future方法。这样当yielded返回结果时,就会回调self.run方法。如果已经返回,就直接执行self.run方法。

self.run方法会将控制权返回给被gen.coroutine的函数。
def run(self):
“”“Starts or resumes the generator, running until it reaches a
yield point that is not ready.
“””
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:
orig_stack_contexts = stack_context._state.contexts
try:
value = future.result()
except Exception:
self.had_exception = True
yielded = self.gen.throw(*sys.exc_info())
else:
yielded = self.gen.send(value)
if stack_context._state.contexts is not orig_stack_contexts:
self.gen.throw(
stack_context.StackContextInconsistentError(
'stack_context inconsistency (probably caused ’
‘by yield within a “with StackContext” block)’))
except (StopIteration, Return) as e:
self.finished = True
self.future = _null_future
if self.pending_callbacks and not self.had_exception:
# If we ran cleanly without waiting on all callbacks
# raise an error (really more of a warning). If we
# had an exception then some callbacks may have been
# orphaned, so skip the check in that case.
raise LeakedCallbackError(
“finished without waiting for callbacks %r” %
self.pending_callbacks)
self.result_future.set_result(getattr(e, ‘value’, None))
self.result_future = None
self._deactivate_stack_context()
return
except Exception:
self.finished = True
self.future = _null_future
self.result_future.set_exc_info(sys.exc_info())
self.result_future = None
self._deactivate_stack_context()
return
if not self.handle_yield(yielded):
return
finally:
self.running = False

这里注意到几个关键地方。
value = future.result()
获取yielded的结果值。
yielded = self.gen.send(value)
使用send方法将结果返回,并且将执行权交给被装饰的函数。并且返回下一个yield的值。

如果函数生成器后面没有yield语句了,就会抛出StopIteration异常。
调用set_result存储结果,返回。
self.result_future.set_result(getattr(e, ‘value’, None))

如果函数生成器后面还有yield语句,就会调用handle_yield,仍然登记self.run方法。
if not self.handle_yield(yielded):
return
之所以要有个判断,是需要在while循环里,如果future已经有返回结果了,就继续取结果,send结果,
一直到future的结果没有返回或者没有yield语句了。

转载于:https://my.oschina.net/u/569730/blog/382661
相关资源:详细解读tornado协程(coroutine)原理_@tornado.gen.coroutine…

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

cang12345

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值