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
版权声明:本文为博主原创文章,转载请附上博文链接!

tornado用户指引(三)------tornado协程使用和原理(二)的更多相关文章

  1. tornado用户指引(二)------------tornado协程实现原理和使用(一)

    摘要:Tornado建议使用协程来实现异步调用.协程使用python的yield关键字来继续或者暂停执行,而不用编写大量的callback函数来实现.(在linux基于epoll的异步调用中,我们需要 ...

  2. PHP下的异步尝试三:协程的PHP版thunkify自动执行器

    PHP下的异步尝试系列 如果你还不太了解PHP下的生成器和协程,你可以根据下面目录翻阅 PHP下的异步尝试一:初识生成器 PHP下的异步尝试二:初识协程 PHP下的异步尝试三:协程的PHP版thunk ...

  3. python并发编程之进程、线程、协程的调度原理(六)

    进程.线程和协程的调度和运行原理总结. 系列文章 python并发编程之threading线程(一) python并发编程之multiprocessing进程(二) python并发编程之asynci ...

  4. GO GMP协程调度实现原理 5w字长文史上最全

    1 Runtime简介 Go语言是互联网时代的C,因为其语法简洁易学,对高并发拥有语言级别的亲和性.而且不同于虚拟机的方案.Go通过在编译时嵌入平台相关的系统指令可直接编译为对应平台的机器码,同时嵌入 ...

  5. Golang源码探索(二) 协程的实现原理

    Golang最大的特色可以说是协程(goroutine)了, 协程让本来很复杂的异步编程变得简单, 让程序员不再需要面对回调地狱, 虽然现在引入了协程的语言越来越多, 但go中的协程仍然是实现的是最彻 ...

  6. Golang源码探索(二) 协程的实现原理(转)

    Golang最大的特色可以说是协程(goroutine)了, 协程让本来很复杂的异步编程变得简单, 让程序员不再需要面对回调地狱,虽然现在引入了协程的语言越来越多, 但go中的协程仍然是实现的是最彻底 ...

  7. 深入浅出!从语义角度分析隐藏在Unity协程背后的原理

    Unity的协程使用起来比较方便,但是由于其封装和隐藏了太多细节,使其看起来比较神秘.比如协程是否是真正的异步执行?协程与线程到底是什么关系?本文将从语义角度来分析隐藏在协程背后的原理,并使用C++来 ...

  8. 协程概念,原理及实现(c++和node.js实现)

    协程 什么是协程 wikipedia 的定义: 协程是一个无优先级的子程序调度组件,允许子程序在特点的地方挂起恢复. 线程包含于进程,协程包含于线程.只要内存足够,一个线程中可以有任意多个协程,但某一 ...

  9. tornado用户指引(四)------tornado协程使用和原理(三)

    版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/happyAnger6/article/details/51291221几种常用的协程方式: 1.回调 ...

随机推荐

  1. 139.00.007 Git学习-Cheat Sheet

    @(139 - Environment Settings | 环境配置) Git虽然极其强大,命令繁多,但常用的就那么十来个,掌握好这十几个常用命令,你已经可以得心应手地使用Git了. 友情附赠国外网 ...

  2. c/c++ 按照行读取文件

    本文代码都在Windows/VC++6.0下测试过, 在linux/g++下也没有问题. 但是请一定注意linux和Windows文件格式的区别,比如: 1. 当linux上的代码读取Windows文 ...

  3. python mysql安装

    本文主要介绍不同系统mysql安装 mac安装mysql http://blog.csdn.net/pansanday/article/details/54915916   linux安装mysql ...

  4. 微软智能云Azure – 中国首家官方支持CoreOS的公有云

    北京2016年6月24日, 在由中国开源软件推进联盟(COPU)主办, 开源社协办,微软赞助的“第十一届开源中国开源世界高峰论坛”上,微软亚太研发集团云计算高级总监梁戈碧女士正式对外宣布一个令人振奋的 ...

  5. 网络安全-使用HTTP动词篡改的认证旁路

    这个东西去年的安全扫描都没有,今天就扫出来了,非常奇怪的一个东西.好吧,找资料找原因.结果可能应为搜索名词的原因,这个问题在群友的帮助下解决了. 在我理解中servlet只有post和get方法,然后 ...

  6. 捕获Task.WhenALl返回的Task的Exception

    如果有一个任务抛出异常,则Task.WhenAll 会出错,并把这个异常放在返回的Task 中.如果多个任务抛出异常,则这些异常都会放在返回的Task 中.但是,如果这个Task 在被await 调用 ...

  7. Eclipse 中 SVN 提交过滤

  8. UIScrollView中的手势

    UIScrollView中的手势 UIScrollView自带了两个手势,分别为: UIPanGestureRecognizer UIPinchGestureRecognizer 他们都是readon ...

  9. (转)从Python的0.1输出0.1000000000000001说浮点数的二进制

    原文地址:http://blog.csdn.net/u012843100/article/details/60885763 今天在学习Python核心编程的时候,十进制浮点数那段看到一个有趣的事情. ...

  10. Spring学习总结之---装配Bean

    Spring配置的可选方案 前言:Spring容器负责创建应用程序中的bean并通过DI来协调这些对象之间的关系,作为开发人员,你需要告诉Spring容器要创建那些Bean,以哪种方式创建,并且如何将 ...