简单介绍Python的Tornado框架中的协程异步实现原理


Posted in Python onApril 23, 2015

Tornado 4.0 已经发布了很长一段时间了, 新版本广泛的应用了协程(Future)特性. 我们目前已经将 Tornado 升级到最新版本, 而且也大量的使用协程特性.

很长时间没有更新博客, 今天就简单介绍下 Tornado 协程实现原理, Tornado 的协程是基于 Python 的生成器实现的, 所以首先来回顾下生成器.
生成器

Python 的生成器可以保存执行状态 并在下次调用的时候恢复, 通过在函数体内使用 yield 关键字 来创建一个生成器, 通过内置函数 next 或生成器的 next 方法来恢复生成器的状态.

def test():
  yield 1

我们调用 test 函数, 此时并不会返回结果, 而是会返回一个生成器

>>> test()
<generator object test at 0x100b3b320>

我们调用其 next 方法则返回 yield 关键字之后的内容.

>>> t = test()
>>> t.next()
1

如果我们接着调用 next 方法, 后面又没有 yield 关键字继续返回的话, 会抛出一个 StopIteration 异常.

yield 关键字不仅仅能从生成器内部返回状态, 同时也可以将外部信息传递到生成器内部, 通过将 yeild 关键里赋值给变量, 并调用生成器的 send 方法来将对象传递到生成器 内部. 需要注意的是生成器的开始必须调用其 next 方法, 后面 send 方法调用的同时 也会触发 next 动作. 如果没有变量接收 yield 关键字那么 send 传递的值将会 被丢弃.

>>> def test():
  a = yield
  print(a)

首先调用 next 上面函数返回的生成器将返回 None, 如果这时候直接调用 next 将 会给生成器发送 None, 如果调用 send 发送一个值, 将打印这个值并抛出 StopIteration 异常.
一个简单地协程

以上就是实现协程的所有基础, 为了加深理解, 我们这里写一个小例子, 例子我们只使用协程 开启两个甚至多个死循环, 下面就是一个极其简单地例子::

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from __future__ import absolute_import, print_function, division, with_statement

def loop1():
  """ 循环1负责抛出一个函数和对应的参数, 并接收结果
  """
  a = 0
  ret = 1
  while True:
    ret = yield sum, [a, ret]
    a, ret = ret, a
    print("Loop1 ret", ret)
def loop2():
  """ 循环2 负责接收函数并计算结果, 然后 yield 出结果
  """
  while True:
    func, args = yield
    yield func(args)
    print("Loop2")


l1 = loop1()
l2 = loop2()
tmp = l1.next()

for i in range(10):
  l2.next()
  ret = l2.send(tmp)
  tmp = l1.send(ret)

上面例子里 loop1 负责产生任务, loop2 负责执行任务, 主循环负责调度任务并将任务结果发回给 任务产生者.
Tornado 如何做的

我们首先看一个使用 Tornado 协程异步的例子

#!/usr/bin/env python
# -*- coding:utf-8 -*-

from __future__ import absolute_import, print_function, division, with_statement

from tornado import gen
from tornado import web
from tornado import httpclient


class ActionHandler(web.RequestHandler):

  @gen.coroutine
  def get(self):
    response = yield httpclient.AsyncHTTPClient().fetch("http://www.linuxzen.com")

    # ...

其实原理在上面简单地例子里已经讲清楚了, 我们来简单分析一遍上面的例子, 首先 Tornado 得到 ActionHandler.get 方法抛出(next)的一个任务, 然后异步的去执行任务, 当任务(网络请求)结束或 异常时 Tornado 取得事件通知然后将结果放回(send)到该方法中让该方法继续执行.

由于是异步的, 调用这个方法并不会阻塞其他任务执行.

这时候我们的方法其实就是上个例子 loop1 函数, 而 Tornado 调度并执行了其抛出的任务.
总结

Tornado 的协程异步可以让异步看起来是顺序执行的, 可以从一大串的 callback 中解脱出来.

Tornado 的协程异步并不是这三言两语能说清楚的, 其中有很复杂的封装和传递, 有兴趣可以自己 阅读源码.

Python 相关文章推荐
Python开发实例分享bt种子爬虫程序和种子解析
May 21 Python
Python中tell()方法的使用详解
May 24 Python
Python 提取dict转换为xml/json/table并输出的实现代码
Aug 28 Python
利用python求解物理学中的双弹簧质能系统详解
Sep 29 Python
python实现图片处理和特征提取详解
Nov 13 Python
python自动化工具之pywinauto实例详解
Aug 26 Python
postman传递当前时间戳实例详解
Sep 14 Python
Tensorflow不支持AVX2指令集的解决方法
Feb 03 Python
python通过对字典的排序,对json字段进行排序的实例
Feb 27 Python
Numpy一维线性插值函数的用法
Apr 22 Python
matplotlib部件之套索Lasso的使用
Feb 24 Python
Python Pygame实战之塔防游戏的实现
Mar 17 Python
解决Python中由于logging模块误用导致的内存泄露
Apr 23 #Python
粗略分析Python中的内存泄漏
Apr 23 #Python
使用beaker让Facebook的Bottle框架支持session功能
Apr 23 #Python
用Python编写脚本使IE实现代理上网的教程
Apr 23 #Python
在Python的Bottle框架中使用微信API的示例
Apr 23 #Python
最基础的Python的socket编程入门教程
Apr 23 #Python
利用Python实现简单的相似图片搜索的教程
Apr 23 #Python
You might like
syphon 虹吸式咖啡冲泡冲煮倒水的得与失
2021/03/03 冲泡冲煮
深入了解 register_globals (附register_globals=off 网站打不开的解决方法)
2012/06/27 PHP
基于PHP实现通过照片获取ip地址
2016/04/26 PHP
PHP实现登录搜狐广告获取广告联盟数据的方法【附demo源码】
2016/10/14 PHP
PHP设计模式之状态模式定义与用法详解
2018/04/02 PHP
从Ajax到JQuery Ajax学习
2007/02/14 Javascript
javascript的函数、创建对象、封装、属性和方法、继承
2011/03/10 Javascript
jQuery 1.5 源码解读 面向中高阶JSER
2011/04/05 Javascript
JS中prototype关键字的功能介绍及使用示例
2013/07/21 Javascript
可兼容IE的获取及设置cookie的jquery.cookie函数方法
2013/09/02 Javascript
js实现类似于add(1)(2)(3)调用方式的方法
2015/03/04 Javascript
jQuery选择器源码解读(五):tokenize的解析过程
2015/03/31 Javascript
js两种拼接字符串的简单方法(必看)
2016/09/02 Javascript
Sortable.js拖拽排序使用方法解析
2016/11/04 Javascript
node+express制作爬虫教程
2016/11/11 Javascript
浅谈vue中使用图片懒加载vue-lazyload插件详细指南
2017/10/23 Javascript
js点击时关闭该范围下拉菜单之外的菜单方法
2018/01/11 Javascript
前端MVVM框架解析之双向绑定
2018/01/24 Javascript
js装饰设计模式学习心得
2018/02/17 Javascript
layui在form表单页面通过Validform加入简单验证的方法
2019/09/06 Javascript
Python中针对函数处理的特殊方法
2014/03/06 Python
python自动安装pip
2014/04/24 Python
Python3处理文件中每个词的方法
2015/05/22 Python
django开发教程之利用缓存文件进行页面缓存的方法
2017/11/10 Python
深入理解Python中的 __new__ 和 __init__及区别介绍
2018/09/17 Python
Python读取mat文件,并保存为pickle格式的方法
2018/10/23 Python
在Tensorflow中实现梯度下降法更新参数值
2020/01/23 Python
Python类绑定方法及非绑定方法实例解析
2020/10/09 Python
一篇文章带你学习CSS3图片边框
2020/11/04 HTML / CSS
大二学期个人自我评价
2014/01/13 职场文书
咖啡厅创业计划书范本
2014/01/22 职场文书
房屋出售协议书
2014/04/10 职场文书
优秀员工评优方案
2014/06/13 职场文书
在校大学生才艺比赛策划书怎么写?
2019/08/26 职场文书
【DOTA2】半决赛强强对话~ PSG LGD vs EHOME - DPC 2022 CN REGIONAL FINALS WINTER
2022/04/02 DOTA
Windows Server 2012 R2 磁盘分区教程
2022/04/29 Servers