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. 【Machine Learning】训练集 验证集 测试集区别

    最近在Udacity上学习Machine learning课程,对于验证集.测试集和训练集的相关概念有些模糊.故整理相关资料如下. 交叉检验(Cross Validation) 在数据分析中,有些算法 ...

  2. 【Python】面向对象编程思想

    概念 "笔"作为一个抽象的概念,可以被看成是一个类.而一支实实在在的笔,则是"笔"这种类型的对象. 一个类可以有属于它的函数,这种函数被称为类的"方法 ...

  3. myeclipse 下载 checkstyle 引入后不显示问题

    参照这篇博客,http://blog.csdn.net/zzq900503/article/details/42003499 下载最新的checkstyle版本后,在本地导入后一直不显示,步骤什么的也 ...

  4. dubbo学习总结三 消费端

    消费端跟服务端类似 注意点是dubbo:reference 和服务端的dubbo:service做区分 消费端主要是处理发送过来的请求

  5. 关于YARN的基本结构

  6. 【Leetcode】【Medium】Longest Substring Without Repeating Characters

    Given a string, find the length of the longest substring without repeating characters. For example, ...

  7. QT的组件布局

    在QT的IDE下,编写一个自定义布局. #include<QApplication> #include<QWidget> #include<QSpinBox> #i ...

  8. Python学习---重点模块之configparse

    configparse模块常用于生成和修改常见的配置文档 生成配置模块:用字典写 import configparser config = configparser.ConfigParser() co ...

  9. TIAGO机器人传感器参数简介 手册翻译

    本来认为这篇文章是最没人气的,竟然收到了回复,看来要继续更新本文了.留下笔者联系方式,邮箱leop22@163.com,欢迎邮件交流. 防止不良爬虫,原文链接:http://www.cnblogs.c ...

  10. maven添加oracle依赖失败

    由于Oracle授权问题,Maven3不提供oracle JDBC driver 可以到maven中央仓库去下载依赖,网址:http://repo.spring.io/plugins-release/ ...