转自:http://blog.xiaogaozi.org/2012/09/21/understanding-tornado-dot-gen/

理解 tornado.gen

SEP 21ST, 2012

Tornado 通过 @asynchronous decorator 来实现异步请求,但使用的时候必须将 request handler 和 callback 分离开,tornado.gen 模块可以帮助我们在一个函数里完成这两个工作。下面是官方的一个例子:

1
2
3
4
5
6
7
8
class GenAsyncHandler(RequestHandler):
@asynchronous
@gen.engine
def get(self):
http_client = AsyncHTTPClient()
response = yield gen.Task(http_client.fetch, "http://example.com")
do_something_with_response(response)
self.render("template.html")

这里用到了两个 decorator 稍显复杂,第一个 @asynchronous 会首先被执行,它的主要工作就是将 RequestHandler 的 _auto_finish 属性置为 false,如下:

web.pydownload

1
2
3
4
5
6
7
8
9
10
def asynchronous(method):
@functools.wraps(method)
def wrapper(self, *args, **kwargs):
if self.application._wsgi:
raise Exception("@asynchronous is not supported for WSGI apps")
self._auto_finish = False
with stack_context.ExceptionStackContext(
self._stack_context_handle_exception):
return method(self, *args, **kwargs)
return wrapper

接着就是最重要的 @gen.engine,这里充分利用了 generator 的各种特性,首先来看 @gen.engine 的实现(我删减了部分代码以简化理解):

gen.pydownload

1
2
3
4
5
6
7
8
9
def engine(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
gen = func(*args, **kwargs)
if isinstance(gen, types.GeneratorType):
runner = Runner(gen)
runner.run()
return
return wrapper

局部变量 gen 代表第一段代码里的 get 函数,因为 get 包含了 yield 语句,因此成为了一个 generator。注意这里 get 并没有被执行,只是赋给了 gen。接下来是运行 Runner 对象的 run 函数。在理解 run 之前需要知道 generator 是通过调用 next() 或者 send() 来启动,启动之后会在遇到 yield 的地方 hold 住,然后将 yield 后面的语句的返回值返回给调用者,generator 此时即处于暂停运行状态,所有上下文都会保存。再次调用 next() 或 send() 便会恢复 generator 的运行,如果不再遇到 yield 语句就会抛出 StopIteration 异常。在恢复运行的同时 yield 语句本身会有返回值,如果是通过调用 next() 来恢复的,那么返回值永远是 None,而如果是通过 send() 则返回值取决于传给 send() 的参数。更多关于 generator 的说明请参考官方文档

结合第一段的示例代码,可以想到 run 干的工作可能就是启动 generator,然后获得 gen.Task对象并调用 http_client.fetch 函数,等回调回来之后恢复 generator 的运行,最后将回调的返回值通过 send() 赋给 response。下面是我简化后的代码。

gen.pydownload

1
2
3
4
5
6
7
8
9
10
11
12
def run(self):
while True:
if not self.yield_point.is_ready():
return
next = self.yield_point.get_result()
try:
yielded = self.gen.send(next)
except StopIteration:
return
if isinstance(yielded, YieldPoint):
self.yield_point = yielded
self.yield_point.start(self)

第 3 行检查回调是否完成,第一次运行 run 总是会返回 True。第 5 行获取回调的返回值,同样的第一次运行返回的是 None。将 None 传给 send() 启动 generator,yielded 即是 gen.Task 对象,第 12 行调用 start 开始运行我们真正需要运行的函数,对应到示例代码就是http_client.fetch 函数,同时将 Runner 的 result_callback 作为回调函数。如下:

gen.pydownload

1
2
3
4
5
6
7
8
9
10
11
def result_callback(self, key):
def inner(*args, **kwargs):
if kwargs or len(args) > 1:
result = Arguments(args, kwargs)
elif args:
result = args[0]
else:
result = None
self.results[key] = result
self.run()
return inner

在得到回调返回值之后再次调用 run,通过 get_result 获取返回值,最后将返回值返回赋给 response,继续 request handler 的代码流程。

理解 tornado.gen的更多相关文章

  1. tornado.gen 模块解析

    转自:http://strawhatfy.github.io/2015/07/22/Tornado.gen/ 引言 注:正文中引用的 Tornado 代码除特别说明外,都默认引用自 Tornado 4 ...

  2. 深入理解Tornado——一个异步web服务器

    本人的第一次翻译,转载请注明出处:http://www.cnblogs.com/yiwenshengmei/archive/2011/06/08/understanding_tornado.html原 ...

  3. Tornado @tornado.gen.coroutine 与 yield

    在使用 Tornado 的过程中产生了以下疑问: 什么时候需要给函数增加 @tornado.gen.coroutine 什么时候调用函数需要 yield @tornado.gen.coroutine ...

  4. Python之路(四十一):通过项目来深入理解tornado

    Tornado之路   引子 与其感慨路难行,不如马上出发 目录 通过项目来深入理解tornado(一):tornado基础回顾 通过项目来深入理解tornado(二):AsyncHttpClient ...

  5. 如何捕捉@tornado.gen.coroutine里的异常

    from tornado import gen from tornado.ioloop import IOLoop @gen.coroutine def throw(a,b): try: a/b ra ...

  6. 理解tornado

    计算密集型:多进程 IO密集型:多线程   能产生IO阻塞的情况很多,比如网络通讯.磁盘读写.当发生阻塞时,CPU是闲置的,此时如果就一个线程就没法处理其他事情了. 所以对于含有IO阻塞的环境,多线程 ...

  7. tornado.gen.coroutine-协程

    http://blog.csdn.net/seeground/article/details/49488281  

  8. 深入理解yield(三):yield与基于Tornado的异步回调

    转自:http://beginman.cn/python/2015/04/06/yield-via-Tornado/ 作者:BeginMan 版权声明:本文版权归作者所有,欢迎转载,但未经作者同意必须 ...

  9. tornado 杂记

    一.建立一个简单的 hello world 网页 ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 import tornado.iolo ...

随机推荐

  1. DialogFragment详解

    详解一: Android提供alert.prompt.pick-list,单选.多选,progress.time-picker和date-picker对话框,并提供自定义的dialog.在Androi ...

  2. Python 文件路径

    # 文件路径: # 1. 相对路径: 相对于当前程序所在的文件夹, 如果在文件夹内, 随便找, 直接写名字 # 如果不在这个文件夹内, 可能需要出文件夹或者进文件夹 # 出文件夹 ../ # 进文件夹 ...

  3. 基础练习 Huffuman树

     基础练习 Huffuman树   时间限制:1.0s   内存限制:512.0MB        问题描述 Huffman树在编码中有着广泛的应用.在这里,我们只关心Huffman树的构造过程. 给 ...

  4. ADC内设与外设的区别

    ADCCLK最大等于14,采样周期为1.5. ADC分两个通道组:规则通道组和注入通道组.规矩通道组就相当于正常运行,而注入通道就相当于中断. ADC为模数转换,而DAC则为数模转换.在芯片引脚中,所 ...

  5. IDEA错误:Cannot start compilation: the output path is not specified for module "XXX".

    错误是发生在从github上checkout自己的项目时.因为没有将配置文件一起上传,所以在运行java程序时有了这个报错: Cannot start compilation: the output ...

  6. node启动时候报错 Error: Cannot find module 'express'

    cmd命令  到目录下,然后运行 npm install -d 再 node hello.js

  7. setsebool命令详解与SELinux管理

    setsebool命令是用来修改SElinux策略内各项规则的布尔值.setsebool命令和getsebool命令是SELinux修改和查询布尔值的一套工具组.SELinux的策略与规则管理相关命令 ...

  8. 2、let 和 const 命令

    let 命令 块级作用域 const 命令 顶层对象的属性 global 对象 let 命令 基本用法 ES6 新增了let命令,用来声明变量.它的用法类似于var,但是所声明的变量,只在let命令所 ...

  9. Tomcat配置JNDI数据源的三种方式-转-http://blog.51cto.com/xficc/1564691

    第一种,单个应用独享数据源 就一步,找到Tomcat的server.xml找到工程的Context节点,添加一个私有数据源 Xml代码   <Context docBase="WebA ...

  10. proc文件系统漫谈

    1. /proc/buddyinfo:/proc/buddyinfo是linuxbuddy系统管理物理内存的debug信息. 在linux中使用buddy算法解决物理内存的外碎片问题,其把所有空闲的内 ...