Python3.5  async和await

async和await是python3.5引入的2个新的关键字(用这两个关键字编写的函数也称之为"原生协程").

从tornado4.3开始,你可以在使用yield的tornado协程中使用这两个关键字。只需将原来用@gen.coroutine装饰的函数定义成async def func(),并将原来yield语句改为await即可。

本文的后面部分为了和老版本的python兼容将会继续使用yield关键字,但是使用async和await将会更快。

举例:

from tornado.ioloop import IOLoop
from tornado.httpclient import AsyncHTTPClient

async def fetch_coroutine():
    http_cli = AsyncHTTPClient()
    reponse = await http_cli.fetch("http://www.baidu.com")
    print(reponse.body)

IOLoop.instance().run_sync(fetch_coroutine)
yield关键字比await关键字更加通用。比如,在基于yield的协程中你可以yield一个Future的列表,但是在原生的协程中要想这么做,你必须用tornado.gen.multi来装饰Future的列表。
另外,你可以使用tornado.gen.convert_yielded来装饰任何可以用yield关键字工作的对象,这样你就可以在await中使用它们。
尽管原生的协程不与任何特定的框架绑定(比如tornado框架,原生协程也不需要额外的tornado.gen.coroutine或者asyncio.coroutine装饰器来装饰).但是不同实现的协程之间是不兼容的。
当第一个协程被调用时会选择一个协程的执行体,然后这个执行体会被通过await调用的其它协程所共享。
tornado的协程执行体被设计的十分通用,它可以接受其它任何框架的await的对象。
其它的协程实现可能有局限性(比如asyncio协程的执行体不能接受其它框架的协程)。
因此,我们建议使用tornado的协程执行体,这样你就可以在你的应用中使用不同框架的协程。
如果你已经使用了asyncio执行体来执行协程,这时你可以通过tornado.platform.asyncio.to_asyncio_future将
tornado的协程适配成可以在asyncio中执行的协程。
举例:
下面的例子演示了如何asyncio中执行tornado的协程:
注意:执行ASyncIOMainLoop().install是为了初始化ioLoop为asyncio的ioLoop.
from tornado import gen
from tornado.platform.asyncio import to_asyncio_future,AsyncIOMainLoop
from tornado.httpclient import AsyncHTTPClient
import asyncio

@gen.coroutine
def tornado_coroutine():
    cli = AsyncHTTPClient()
    response = yield cli.fetch("http://www.baidu.com")
    print(response.body)

AsyncIOMainLoop().install()
asyncio.get_event_loop().run_until_complete(to_asyncio_future(tornado_coroutine()))

tornado协程的工作原理
包含yield语句的函数是一个生成器。所有的生成器都是异步的。当我们调用生成器函数的时候,生成器函数返回一个生成器对象,而不是像普通函数那样直接执行完成。@gen.coroutine装饰器通过yield表达式来和生成器对象交互,调用@gen.coroutine装饰的函数会返回一个Future.
下面是一个coroutine装饰器内部循环的简化实现:
可以看到,coroutine装饰器内部有一个Runner对象,这个对象将我们的生成器函数包装为self.gen.
这个对象就是协程的运行体,它会在内部一直循环运行生成器,直到生成器函数阻塞返回一个Future或者结束。如果遇到yield返回的Future,便通过Future对象的结束回调函数来继续运行生成器函数。
具体实现是:
1.当生成器函数运行到yield时,会得到一个Future.为这个Future添加结束回调通知函数callback.执行体释放执行权给ioLoop继续执行其它协程。
2.当这个Future异步运行结束时,会调用callback函数。callback函数得到Future的运行结果,并将结果通过生成器的send方法发送给生成器。这样生成器函数就会在yield的地方返回Future的异步执行结果并继续运行。
3.1接着生成器函数继续运行,直到生成器函数运行结束
3.2或者遇到下一个yield转到步骤1
# Simplified inner loop of tornado.gen.Runner
def run(self):
# send(x) makes the current yield return x.
# It returns when the next yield is reached
    future = self.gen.send(self.next)
    def callback(f):
        self.next = f.result()
        self.run()
    future.add_done_callback(callback)

如何调用一个协程
协程并不以一般的方式产生异常。协程中产生的任何异常将会被Future包装起来直到它被yielded.
下面代码展示了我们函数产生的异常是如何被包装到future中的:
func为我们的函数。
try:
    result = func(*args, **kwargs)
except (Return, StopIteration) as e:
    result = _value_from_stopiteration(e)
except Exception:
    future.set_exc_info(sys.exc_info())
return future
这意味着我们必须以正确的方式调用协程,否则你可能会忽略一些发生的错误。
在大部分情况下,任何调用协程的函数本身也需要是一个协程,而且需要在调用另外一个协程的地方使用yield关键字。当你需要重写父类中的函数时,你需要翻阅相应的文档以确定函数是否允许被实现为一个协程,文档中需要说明函数需要是一个协程或者需要返回一个Future.
下面的函数修正了上面的错误,在一个协程中通过yield关键字调用divide这个协程。
@gen.coroutine
def good_call():
    # yield will unwrap the Future returned by divide() and raise
    # the exception.
    yield divide(1, 0)
有时候你仅仅是想执行一个协程并不关心其结果,这种情况建议你使用IOLoop.spawn_callback(callback, *args, **kwargs)。这样如果协程执行失败,ioLoop会将调用栈记录到log中。
# The IOLoop will catch the exception and print a stack trace in
# the logs. Note that this doesn't look like a normal call, since
# we pass the function object to be called by the IOLoop.
IOLoop.current().spawn_callback(divide, 1, 0)

最后,如果在程序顶层,ioloop还没有运行,你可以通过run_sync方法开始ioloop的运行,并执行协程。run_sync常常用来执行main协程,main里面包含了一系列的协程。

# run_sync() doesn't take arguments, so we must wrap the
# call in a lambda.
IOLoop.current().run_sync(lambda: divide(1, 0))
注意:由于run_sync只接受一个函数参数,所以你需要通过lambda表达式传递函数参数,或者通过functools.partial来将函数变为偏函数。
IOLoop.current().run_sync(functools.partial(divide,1, 0))
---------------------
作者:self-motivation
来源:CSDN
原文:https://blog.csdn.net/happyanger6/article/details/51277407
版权声明:本文为博主原创文章,转载请附上博文链接!

最新文章

  1. js中的navigator对象
  2. sae评测报告-2013最新版
  3. The last packet successfully received from the server was 2,926,157 milliseconds ago. The last packet sent successfully to the server was 2,926,158 milliseconds ago. is longer than the server configured value of 'wait_timeout'. 解决办法
  4. Android关联源码support-v4的问题解决
  5. CRUD之delete操作
  6. 从Jetty、Tomcat和Mina中提炼NIO构架网络服务器的经典模式(三)
  7. hibernate 实体关系映射笔记
  8. Spring(3.2.3) - Beans(6): 作用域
  9. 比较两个字符,相等输出yes,不相等输出no
  10. TweenMax.js
  11. 前向分步算法 && AdaBoost算法 && 提升树(GBDT)算法 && XGBoost算法
  12. js 对象与json的转化
  13. hexo——轻量、简易、高逼格的博客
  14. vue全家桶+Koa2开发笔记(8)--开发网页
  15. 缩点+染色+DFS codeforce467D
  16. A - Excellent Team
  17. 机器学习初入门02 - Pandas的基本操作
  18. 【转】安全加密(一):这些MCU加密方法你都知道吗?
  19. VM页面中遍历枚举类
  20. (原创)Windows下使用android ADT工具dmtracedump.exe绘图

热门文章

  1. Linux制作deb
  2. dubbo学习总结三 消费端
  3. Windows IO 性能简单测试
  4. SQL Server ->> 尝试优化ETL中优化Merge性能
  5. js笔记 标签: javascript 2016-08-01 13:30 75人阅读 评论(0) 收藏
  6. How to update BOL entity property value via ABAP code
  7. 可跨域的单点登录(SSO)实现方案
  8. HTML DOM 初学笔记
  9. UIView的多重属性
  10. fastcgi c/c++ API 说明