tornado的gen.engine浅析

1. 首先看看示例代码,也是官方给出的:

 1 class Main(RequestHandler):
 2     @tornado.web.asynchronous
 3     @gen.engine
 4     def get(self):
 5         if _DEBUG:
 6             pdb.set_trace()
 7         http_client = AsyncHTTPClient()
 8         response = yield gen.Task(http_client.fetch, "http://code.rootk.com")
 9         self.write(str(response))
10         self.finish()

 

2. 初始化异步的HTTP客户端,tornado自带的。

http_client = AsyncHTTPClient()

 

3. @gen.engine

这是一个装饰器,它装饰生成器函数,看看实现的代码:

 1 def engine(func):
 2     @functools.wraps(func)
 3     def wrapper(*args, **kwargs):
 4         runner = None
 5 
 6         def handle_exception(typ, value, tb):
 7             # if the function throws an exception before its first "yield"
 8             # (or is not a generator at all), the Runner won't exist yet.
 9             # However, in that case we haven't reached anything asynchronous
10             # yet, so we can just let the exception propagate.
11             if runner is not None:
12                 return runner.handle_exception(typ, value, tb)
13             return False
14         with ExceptionStackContext(handle_exception) as deactivate:
15             # gen是一个RequestHandler的子类的方法
16             gen = func(*args, **kwargs)
17             # 如果方法是一个生成器
18             if isinstance(gen, types.GeneratorType):
19                 # 实例化Runner类,传入生成器作为参数
20                 runner = Runner(gen, deactivate)
21                 runner.run()
22                 return
23             assert gen is None, gen
24             deactivate()
25             # no yield, so we're done
26     return wrapper

(1). 在代码runner = Runner(gen, deactivate)之中,gen指的是示例代码中get这个生成器函数。

(2). 然后调用实例的run方法。

 

4. 分析一下类的Runner实例的run方法。

run函数循环会执行三遍:
第一遍,当gen.engine执行时,遇到runner.run()会执行一遍,最后会调用Task的start方法
这次执行的结果是让示例代码中的yield语句运行完毕。

第二遍,start方法执行完毕后,http_client.fetch方法会回调Runner的result_callback方法,会再次执行。
这次执行的结果是向生成器函数发送执行结果:
yielded = self.gen.send(next)
next的内容就是在上面的代码中:
next = self.yield_point.get_result()
而且self.finished = True,被设置为True。

第三遍,最后start方法执行完毕(它是由第一步调用的),run方法里的循环会最后一次运行。
当它发现self.finished被设置为True时,直接return。

实现代码:

 1 if self.running or self.finished:
 2             return
 3         try:
 4             self.running = True
 5             while True:
 6                 # 如果上一次执行没有抛出异常
 7                 if self.exc_info is None:
 8                     try:
 9                         if not self.yield_point.is_ready():
10                             return
11                         # self.yield_point是一个_NullYieldPoint类的实例
12                         # 实例的get_result方法总是返回None
13                         next = self.yield_point.get_result()
14                     except Exception:
15                         self.exc_info = sys.exc_info()
16                 try:
17                     if self.exc_info is not None:
18                         self.had_exception = True
19                         exc_info = self.exc_info
20                         self.exc_info = None
21                         yielded = self.gen.throw(*exc_info)
22                     else:
23                         # 这里调用生成器函数的send方法,参数为None
24                         # 对应到示例中的代码,就完整的执行了下面这一句
25                         # yield gen.Task(http_client.fetch, "http://code.rootk.com")
26                         # 返回了一个Task类的实例
27                         # 因为可以同时执行多个任务,所以返回的也有可能是含有一个Task类的列表
28                         # 下面会做判断
29                         yielded = self.gen.send(next)
30                 except StopIteration:
31                     self.finished = True
32                     if self.pending_callbacks and not self.had_exception:
33                         # If we ran cleanly without waiting on all callbacks
34                         # raise an error (really more of a warning).  If we
35                         # had an exception then some callbacks may have been
36                         # orphaned, so skip the check in that case.
37                         raise LeakedCallbackError(
38                             "finished without waiting for callbacks %r" %
39                             self.pending_callbacks)
40                     self.deactivate_stack_context()
41                     return
42                 except Exception:
43                     self.finished = True
44                     raise
45                 # 判断yield返回的内容
46                 # 如果是列表类型(说明返回了多个任务,即多个Task类的实例)
47                 if isinstance(yielded, list):
48                     yielded = Multi(yielded)
49                 # 如果是YieldPoint类的实例,就是Task类本身(Task类是YieldPoint类的子类)
50                 # 说明只有一个任务(一个Task类的实例)
51                 if isinstance(yielded, YieldPoint):
52                     self.yield_point = yielded
53                     try:
54                         # 调用Task类实例的start方法
55                         # 参数为Runner类实例自身
56                         self.yield_point.start(self)
57                     except Exception:
58                         self.exc_info = sys.exc_info()
59                 else:
60                     self.exc_info = (BadYieldError("yielded unknown object %r" % yielded),)
61         finally:
62             self.running = False

到这里Runner类实例的run方法执行完毕,接下来执行到了Task类实例的start方法。

 

5. 看看Task类实例的start方法的实现代码:

 1 def start(self, runner):
 2     # runner是一个Runner类实例
 3     self.runner = runner
 4         
 5     # object每次返回了不同的python的对象实例
 6     # 每次生成的对象的id都不一样,所以把它当作一个唯一的key
 7     self.key = object()
 8         
 9     # 调用runner的register_callback方法,以key作为参数
10     # 该方法只有一句代码:self.pending_callbacks.add(key)
11     # 在self.pending_callbacks这个set类型数据中,加入key
12     runner.register_callback(self.key)
13         
14     # 加入callback,以key作为参数
15     # result_callback是一个装饰器
16     # 返回的是内层函数
17     # !!!下面的self.func执行成功后,会调用这个返回的内层函数!!!
18     self.kwargs["callback"] = runner.result_callback(self.key)
19     
20     # 执行真正的函数,示例代码中执行的就是http_client.fetch函数
21     # self.kwargs中已经加入了一个关键字参数:'callback'
22     self.func(*self.args, **self.kwargs)

 

6. 负责返回内容的回调函数,就是Runner类实例的result_callback方法,它是一个装饰器。

 1 # 外层函数接受key作为参数
 2 def result_callback(self, key):
 3     # 内层函数负责返回内容
 4     # 由http_client.fetch函数调用
 5     # result就是fetch函数返回的内容
 6     def inner(*args, **kwargs):
 7         if _DEBUG:
 8             pdb.set_trace()
 9         if kwargs or len(args) > 1:
10             result = Arguments(args, kwargs)
11         elif args:
12             result = args[0]
13         else:
14             result = None
15         #最后调用Runner类实例的set_result方法
16         self.set_result(key, result)
17     return inner

 

7. 将返回的内容添加到Runner类实例的result字典中

def set_result(self, key, result):
    """Sets the result for ``key`` and attempts to resume the generator."""
    # 将内容加入到self.results字典中
    self.results[key] = result
    #最后调用一遍run
    self.run()

 

转载于:https://www.cnblogs.com/huazi/archive/2012/12/10/2812146.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值