Python的gevent框架的入门教程


Posted in Python onApril 29, 2015

Python通过yield提供了对协程的基本支持,但是不完全。而第三方的gevent为Python提供了比较完善的协程支持。

gevent是第三方库,通过greenlet实现协程,其基本思想是:

当一个greenlet遇到IO操作时,比如访问网络,就自动切换到其他的greenlet,等到IO操作完成,再在适当的时候切换回来继续执行。由于IO操作非常耗时,经常使程序处于等待状态,有了gevent为我们自动切换协程,就保证总有greenlet在运行,而不是等待IO。

由于切换是在IO操作时自动完成,所以gevent需要修改Python自带的一些标准库,这一过程在启动时通过monkey patch完成:

from gevent import monkey; monkey.patch_socket()
import gevent

def f(n):
  for i in range(n):
    print gevent.getcurrent(), i

g1 = gevent.spawn(f, 5)
g2 = gevent.spawn(f, 5)
g3 = gevent.spawn(f, 5)
g1.join()
g2.join()
g3.join()

运行结果:

<Greenlet at 0x10e49f550: f(5)> 0
<Greenlet at 0x10e49f550: f(5)> 1
<Greenlet at 0x10e49f550: f(5)> 2
<Greenlet at 0x10e49f550: f(5)> 3
<Greenlet at 0x10e49f550: f(5)> 4
<Greenlet at 0x10e49f910: f(5)> 0
<Greenlet at 0x10e49f910: f(5)> 1
<Greenlet at 0x10e49f910: f(5)> 2
<Greenlet at 0x10e49f910: f(5)> 3
<Greenlet at 0x10e49f910: f(5)> 4
<Greenlet at 0x10e49f4b0: f(5)> 0
<Greenlet at 0x10e49f4b0: f(5)> 1
<Greenlet at 0x10e49f4b0: f(5)> 2
<Greenlet at 0x10e49f4b0: f(5)> 3
<Greenlet at 0x10e49f4b0: f(5)> 4

可以看到,3个greenlet是依次运行而不是交替运行。

要让greenlet交替运行,可以通过gevent.sleep()交出控制权:

def f(n):
  for i in range(n):
    print gevent.getcurrent(), i
    gevent.sleep(0)

执行结果:

<Greenlet at 0x10cd58550: f(5)> 0
<Greenlet at 0x10cd58910: f(5)> 0
<Greenlet at 0x10cd584b0: f(5)> 0
<Greenlet at 0x10cd58550: f(5)> 1
<Greenlet at 0x10cd584b0: f(5)> 1
<Greenlet at 0x10cd58910: f(5)> 1
<Greenlet at 0x10cd58550: f(5)> 2
<Greenlet at 0x10cd58910: f(5)> 2
<Greenlet at 0x10cd584b0: f(5)> 2
<Greenlet at 0x10cd58550: f(5)> 3
<Greenlet at 0x10cd584b0: f(5)> 3
<Greenlet at 0x10cd58910: f(5)> 3
<Greenlet at 0x10cd58550: f(5)> 4
<Greenlet at 0x10cd58910: f(5)> 4
<Greenlet at 0x10cd584b0: f(5)> 4

3个greenlet交替运行,

把循环次数改为500000,让它们的运行时间长一点,然后在操作系统的进程管理器中看,线程数只有1个。

当然,实际代码里,我们不会用gevent.sleep()去切换协程,而是在执行到IO操作时,gevent自动切换,代码如下:

from gevent import monkey; monkey.patch_all()
import gevent
import urllib2

def f(url):
  print('GET: %s' % url)
  resp = urllib2.urlopen(url)
  data = resp.read()
  print('%d bytes received from %s.' % (len(data), url))

gevent.joinall([
    gevent.spawn(f, 'https://www.python.org/'),
    gevent.spawn(f, 'https://www.yahoo.com/'),
    gevent.spawn(f, 'https://github.com/'),
])

运行结果:

GET: https://www.python.org/
GET: https://www.yahoo.com/
GET: https://github.com/
45661 bytes received from https://www.python.org/.
14823 bytes received from https://github.com/.
304034 bytes received from https://www.yahoo.com/.

从结果看,3个网络操作是并发执行的,而且结束顺序不同,但只有一个线程。
小结

使用gevent,可以获得极高的并发性能,但gevent只能在Unix/Linux下运行,在Windows下不保证正常安装和运行。

由于gevent是基于IO切换的协程,所以最神奇的是,我们编写的Web App代码,不需要引入gevent的包,也不需要改任何代码,仅仅在部署的时候,用一个支持gevent的WSGI服务器,立刻就获得了数倍的性能提升。具体部署方式可以参考后续“实战”-“部署Web App”一节。

Python 相关文章推荐
Python编程中用close()方法关闭文件的教程
May 24 Python
python Django框架实现自定义表单提交
Mar 25 Python
Python反射用法实例简析
Dec 22 Python
20个常用Python运维库和模块
Feb 12 Python
Python爬虫获取图片并下载保存至本地的实例
Jun 01 Python
Python实现简易过滤删除数字的方法小结
Jan 09 Python
Python使用matplotlib绘制Logistic曲线操作示例
Nov 28 Python
Python使用configparser读取ini配置文件
May 25 Python
python中前缀运算符 *和 **的用法示例详解
May 28 Python
解决PyCharm不在run输出运行结果而不是再Console里输出的问题
Sep 21 Python
sublime3之内网安装python插件Anaconda的流程
Nov 10 Python
Python代码实现双链表
May 25 Python
在Python中使用HTML模版的教程
Apr 29 #Python
以Flask为例讲解Python的框架的使用方法
Apr 29 #Python
详解Python程序与服务器连接的WSGI接口
Apr 29 #Python
Python的SQLAlchemy框架使用入门
Apr 29 #Python
python使用post提交数据到远程url的方法
Apr 29 #Python
python实现根据ip地址反向查找主机名称的方法
Apr 29 #Python
连接Python程序与MySQL的教程
Apr 29 #Python
You might like
PHP在引号前面添加反斜杠(PHP去除反斜杠)
2013/09/28 PHP
PHP限制页面只能在微信自带浏览器访问的代码
2014/01/15 PHP
[原创]PHPCMS遭遇会员投稿审核无效的解决方法
2017/01/11 PHP
php删除一个路径下的所有文件夹和文件的方法
2018/02/07 PHP
自动检查并替换文本框内的字符
2006/06/30 Javascript
jQuery 浮动广告实现代码
2008/12/25 Javascript
jQuery1.9.1针对checkbox的调整方法(prop)
2014/05/01 Javascript
Javascript 读取操作Sql中的Xml字段
2014/10/09 Javascript
JavaScript数组随机排列实现随机洗牌功能
2015/03/19 Javascript
JavaScript通过prototype给对象定义属性用法实例
2015/03/23 Javascript
jQuery实现动画效果circle实例
2015/08/06 Javascript
学习javascript面向对象 javascript实现继承的方式
2016/01/04 Javascript
Ionic如何实现下拉刷新与上拉加载功能
2016/06/03 Javascript
node.js 和HTML5开发本地桌面应用程序
2016/12/13 Javascript
原生js实现放大镜特效
2017/03/08 Javascript
node.js中实现kindEditor图片上传功能的方法教程
2017/04/26 Javascript
Bootstrap组件之下拉菜单,多级菜单及按钮布局方法实例
2017/05/25 Javascript
初学者AngularJS的环境搭建过程
2017/10/27 Javascript
浅谈微信小程序flex布局基础
2018/09/10 Javascript
layer父页获取弹出层输入框里面的值方法
2019/09/02 Javascript
elementUI同一页面展示多个Dialog的实现
2020/11/19 Javascript
详解阿里Node.js技术文档之process模块学习指南
2021/01/04 Javascript
Python计算斗牛游戏概率算法实例分析
2017/09/26 Python
Python中跳台阶、变态跳台阶与矩形覆盖问题的解决方法
2018/05/19 Python
python 读取dicom文件,生成info.txt和raw文件的方法
2019/01/24 Python
在Python中os.fork()产生子进程的例子
2019/08/08 Python
Pandas DataFrame中的tuple元素遍历的实现
2019/10/23 Python
纯CSS3实现自定义Tooltip边框涂鸦风格的教程
2014/11/05 HTML / CSS
ROSEFIELD手表荷兰官方网上商店:北欧极简设计女士腕表品牌
2018/01/24 全球购物
htmlentities() 和 htmlspecialchars()有什么区别
2015/07/01 面试题
美丽家庭事迹材料
2014/05/03 职场文书
客户答谢会致辞
2015/01/20 职场文书
合理化建议书
2015/02/04 职场文书
2015年五四青年节演讲稿
2015/03/18 职场文书
观看焦裕禄观后感
2015/06/09 职场文书
Centos环境下Postgresql 安装配置及环境变量配置技巧
2021/05/18 PostgreSQL