tornado的gen.engine浅析
来源:互联网 发布:高性能浏览器网络 编辑:程序博客网 时间:2024/06/13 22:07
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 asynchronous10 # 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 False14 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 return23 assert gen is None, gen24 deactivate()25 # no yield, so we're done26 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 return11 # self.yield_point是一个_NullYieldPoint类的实例12 # 实例的get_result方法总是返回None13 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 = True19 exc_info = self.exc_info20 self.exc_info = None21 yielded = self.gen.throw(*exc_info)22 else:23 # 这里调用生成器函数的send方法,参数为None24 # 对应到示例中的代码,就完整的执行了下面这一句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 = True32 if self.pending_callbacks and not self.had_exception:33 # If we ran cleanly without waiting on all callbacks34 # raise an error (really more of a warning). If we35 # had an exception then some callbacks may have been36 # 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 return42 except Exception:43 self.finished = True44 raise45 # 判断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 = yielded53 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类型数据中,加入key12 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 = None15 #最后调用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()
0 0
- tornado的gen.engine浅析
- 实现自己的gen.engine和gen.Task
- tornado.gen.Task post
- Tornado用回调代替gen
- 理解 tornado.gen
- Tornado用回调代替gen
- tornado.gen.coroutine-协程
- 使用tornado的gen.coroutine进行异步编程
- tornado浅析
- tornado.gen.coroutine-编写异步函数
- tornado异步机制浅析
- 浅析App Engine
- 浅析tornado协程运行原理
- 浅析tornado协程运行原理
- Tornado学习笔记(一)【tornado的优势】
- Tornado的初步了解
- tornado的安装
- tornado 分页的实现!
- XML基础(一)
- POJ 3979 分数加减法【数学讨论题】
- X5 报表的ACTION 数据返回TABLE 代码
- java内存模型 多处理器
- XML基础(二)
- tornado的gen.engine浅析
- 相关问题及解决办法
- 更改MySQL用户密码
- 使用ngrok让微信公众平台通过80端口访问本机
- CString 成员函数用法大全
- java 内存模型 指南(Recipes)
- Python yield具体使用方法探讨
- 检测到 ContextSwitchDeadlock
- 有关类和方法