tornado.gen.coroutine 的实现原理
''' 装饰器的作用 1,装饰器函数里包含 send() 方法,先next() 才能 sent() 方法 2,装饰器是将异步交给epoll 来进行实现的,通过 ioloop 交给 linux epoll 3,只有此函数是 协程函数,装饰器才能起作用 4,send() 方法是为了起到唤醒的作用的,也就是当 sent() 完成之后交给调度着,已经完成任务 ''' import time from tornado import gen from tornado.ioloop import IOLoop from tornado.httpclient import HTTPClient,AsyncHTTPClient # 装饰器声明这是个协程函数 @gen.coroutine def async_visit(): http_client = AsyncHTTPClient() # 使用 yield使得代码之中不必要编写回调函数,直接在yield 之后进行书写即可 response = yield http_client.fetch('https://www.baidu.com/') print(response.body) def func_normal(): print('start to call async_visit') # 采用了异步形式,不会等待被调用的协程执行完成 # IOLoop.current().spawn_callback(lambda :async_visit()) #0.00044608116149902344 # 同步形式,阻塞当前函数的执行 IOLoop.current().run_sync(lambda :async_visit()) #5.003432750701904 print('end to call async_visit') old_time = time.time() func_normal() print(time.time() - old_time)
1, gen.coroutine 装饰器装饰的函数是实现协程的函数,也就是声明了这就是协程函数
2, gen.coroutine 会遍历函数中所有的yield 函数,并且执行每一个协程函数!
3, 每一个Future对应一个异步操作
4, 该Future对象可以添加回调函数,当该异步操作完成后,需要对该Future对象设置set_done或者set_result,然后执行其所有的回调函数
5, 凡是使用了coroutine装饰器的generator函数都会返回一个Future对象,同时会不断为该generator,该generator每一次运行send()或者next()的返回结果yielded以及future对象运行Runner()
6, Runner()会对generator不断进行send()或者next()操作。具体步骤是:上一个next()或者send()操作返回的yielded(一般是一个Future对象)被set_done后,将该yielded对象的结果send()至generator中,不断循环该操作,直到产生StopIteration或者Return异常(这表示该generator执行结束),这时会为该generator对应的Future对象set_result。
我们可以看到tornado的协程是基于generator的,generator可以通过yield关键字暂停执行,也可以通过next()或者send()恢复执行,同时send()可以向generator中传递值。
而将协程连接起来的纽带则是Future对象,每一个Future对象都对应着一个异步操作,我们可以为该对象添加许多回调函数,当异步操作完成后通过对Future对象进行set_done或者set_result就可以执行相关的回调函数。
提供动力的则是Runner(),他不停的将generator所yield的每一个future对象的结果send()至generator,当generator运行结束,他会进行最后的包装工作,对该generator所对应的Future对象执行set_result操作。
gen.coroutine 源码分析:
1, 首先生成一个Future对象
# future = _create_future() # future 是 Future 的实例对象
2,运行该被装饰函数并将结果赋值给result
# 如果函数 async_visit 是个生成器,那么就会执行 #执行func,若func中包含yield,则返回一个生成器对象!generator对象 #result = func(*args, **kwargs)
3,执行 Runner()可以自动的将异步操作的结果send()至生成器中止的地方
''' 很多情况下会有generator的嵌套。比如说经常会yield 一个generator。当A生成器yield B生成器时,分两步: 1 我们首先中止A的执行转而执行B 2 当B执行完成后,我们需要将B的结果send()至A中止的地方,继续执行A ''' #yielded = next(result) # 装饰函数的第一次yield,将结果赋值给yielded #Runner(result, future, yielded)
4,在 Runner 初始化之中执行了 self.run()方法!
#self.future对象是否已经被done(完成),如果没有的话为其添加回调函数,这个回调函数会执行self.run() # handle_yield(self, yielded) #循环向generator中传递值,直到某个yield返回的yielded还没有被done #self.run()
5,在 self.run()之中不停的执行generator的send()方法,发送的值就是yielded的result
# 获取future对象的结果 #value = future.result() #send该结果,并将self.gen返回的值赋值给yielded(一般情况下这也是个future对象) # yielded = self.gen.send(value)