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实现的解析crontab配置文件代码
Jun 30 Python
Python自动发邮件脚本
Mar 31 Python
Python在groupby分组后提取指定位置记录方法
Apr 20 Python
如何使用pyinstaller打包32位的exe程序
May 26 Python
python2和python3在处理字符串上的区别详解
May 29 Python
linux环境中没有网络怎么下载python
Jul 07 Python
Python简易计算器制作方法代码详解
Oct 31 Python
Python实现计算长方形面积(带参数函数demo)
Jan 18 Python
使用Pycharm分段执行代码
Apr 15 Python
在Ubuntu 20.04中安装Pycharm 2020.1的图文教程
Apr 30 Python
如何使用Python自动生成报表并以邮件发送
Oct 15 Python
Python使用pyecharts控件绘制图表
Jun 05 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一个解析字符串排列数组的方法
2015/05/12 PHP
PHP之密码加密的几种方式
2015/07/29 PHP
深入浅析用PHP实现MVC
2016/03/02 PHP
thinkPHP3.2简单实现文件上传的方法
2016/05/16 PHP
不常用但很实用的PHP预定义变量分析
2019/06/25 PHP
JavaScript高级程序设计 读书笔记之八 Function类及闭包
2012/02/27 Javascript
javascript-简单的计算器实现步骤分解(附图)
2013/05/30 Javascript
解析javascript 数组以及json元素的添加删除
2013/06/26 Javascript
举例说明JavaScript中的实例对象与原型对象
2016/03/11 Javascript
JS阻止事件冒泡行为和闭包的方法
2016/06/16 Javascript
JS实现间歇滚动的运动效果实例
2016/12/22 Javascript
超简单的Vue.js环境搭建教程
2017/03/17 Javascript
jsTree事件和交互以及插件plugins详解
2017/08/29 Javascript
node作为中间服务层如何发送请求(发送请求的实现方法详解)
2018/01/02 Javascript
vue实现验证码按钮倒计时功能
2018/04/10 Javascript
深入理解移动前端开发之viewport
2018/10/19 Javascript
Javascript实现时间倒计时功能
2018/11/17 Javascript
node.js ws模块搭建websocket服务端的方法示例
2019/04/25 Javascript
JS通过ajax + 多列布局 + 自动加载实现瀑布流效果
2019/05/30 Javascript
Node.js API详解之 Error模块用法实例分析
2020/05/14 Javascript
vue v-for出来的列表,点击某个li使得当前被点击的li字体变红操作
2020/07/17 Javascript
vue中echarts的用法及与elementui-select的协同绑定操作
2020/11/17 Vue.js
浅谈用VSCode写python的正确姿势
2017/12/16 Python
Python if语句知识点用法总结
2018/06/10 Python
示例详解Python3 or Python2 两者之间的差异
2018/08/23 Python
pycharm显示远程图片的实现
2019/11/04 Python
TensorFlow2.0:张量的合并与分割实例
2020/01/19 Python
python 6.7 编写printTable()函数表格打印(完整代码)
2020/03/25 Python
python speech模块的使用方法
2020/09/09 Python
阿迪达斯墨西哥官方网站:adidas墨西哥
2017/11/03 全球购物
金融专业个人求职信
2013/09/22 职场文书
大学生专科学习生活的自我评价
2013/12/07 职场文书
奶茶店创业计划书范文
2014/01/17 职场文书
社区志愿者活动总结
2014/06/26 职场文书
朋友离别感言
2015/08/04 职场文书
Python Django 后台管理之后台模型属性详解
2021/04/25 Python