简单了解python gevent 协程使用及作用


Posted in Python onJuly 22, 2019

简介

没有切换开销。因为子程序切换不是线程切换,而是由程序自身控制,没有线程切换的开销,因此执行效率高,

不需要锁机制。因为只有一个线程,也不存在同时写变量冲突,在协程中控制共享资源不加锁,只需要判断状态就好了,所以执行效率比多线程高很多

Python对协程的支持还非常有限,用在generator中的yield可以一定程度上实现协程。

yield

传统的生产者-消费者模型是一个线程写消息,一个线程取消息,通过锁机制控制队列和等待,但一不小心就可能死锁。

如果改用协程,生产者生产消息后,直接通过yield跳转到消费者开始执行,待消费者执行完毕后,切换回生产者继续生产,效率极高

代码

import time

def consumer():
  r = ''
  while True:
    n = yield r
    if not n:
      return
    print('[CONSUMER] Consuming %s....' % n)
    r = '200 OK'

def produce(c):
  c.next()
  n = 0
  while n < 5:
    n = n + 1
    print('[PRODUCER] Producing %s...' % n)
    r = c.send(n)
    print('[PRODUCER] Consumer return: %s\n' % r)
  c.close()

if __name__=='__main__':
  c = consumer()
  produce(c)

结果

[PRODUCER] Producing 1...
[CONSUMER] Consuming 1....
[PRODUCER] Consumer return: 200 OK

[PRODUCER] Producing 2...
[CONSUMER] Consuming 2....
[PRODUCER] Consumer return: 200 OK

[PRODUCER] Producing 3...
[CONSUMER] Consuming 3....
[PRODUCER] Consumer return: 200 OK

[PRODUCER] Producing 4...
[CONSUMER] Consuming 4....
[PRODUCER] Consumer return: 200 OK

[PRODUCER] Producing 5...
[CONSUMER] Consuming 5....
[PRODUCER] Consumer return: 200 OK

分析

  • 首先调用c.next()启动生成器
  • 然后,一旦生产了东西,通过c.send(n)切换到consumer执行
  • consumer通过yield拿到消息,处理,又通过yield把结果传回
  • produce拿到consumer处理的结果,继续生产下一条消息

整个过程无锁,由一个线程执行,producer和consumer写作完成任务,所以叫做协程

gevent

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

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

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

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 0x7f7216efbe10: f(5)> 0
<Greenlet at 0x7f7216efbe10: f(5)> 1
<Greenlet at 0x7f7216efbe10: f(5)> 2
<Greenlet at 0x7f7216efbe10: f(5)> 3
<Greenlet at 0x7f7216efbe10: f(5)> 4
<Greenlet at 0x7f720f54e0f0: f(5)> 0
<Greenlet at 0x7f720f54e0f0: f(5)> 1
<Greenlet at 0x7f720f54e0f0: f(5)> 2
<Greenlet at 0x7f720f54e0f0: f(5)> 3
<Greenlet at 0x7f720f54e0f0: f(5)> 4
<Greenlet at 0x7f720f54e190: f(5)> 0
<Greenlet at 0x7f720f54e190: f(5)> 1
<Greenlet at 0x7f720f54e190: f(5)> 2
<Greenlet at 0x7f720f54e190: f(5)> 3
<Greenlet at 0x7f720f54e190: f(5)> 4

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

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

import gevent

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

g1 = gevent.spawn(f, 3)
g2 = gevent.spawn(f, 3)
g3 = gevent.spawn(f, 3)

g1.join()
g2.join()
g3.join()

结果

<Greenlet at 0x7f74e2179e10: f(3)> 0
<Greenlet at 0x7f74da7cb0f0: f(3)> 0
<Greenlet at 0x7f74da7cb190: f(3)> 0
<Greenlet at 0x7f74e2179e10: f(3)> 1
<Greenlet at 0x7f74da7cb0f0: f(3)> 1
<Greenlet at 0x7f74da7cb190: f(3)> 1
<Greenlet at 0x7f74e2179e10: f(3)> 2
<Greenlet at 0x7f74da7cb0f0: f(3)> 2
<Greenlet at 0x7f74da7cb190: f(3)> 2

可以看出3个greenlet是交替执行

如果把循环改为1000,让执行次数执行时间长些,查看进程,可以看到线程只有一个。

当然,实际代码中,不可能用gevent.sleep()去切换协程,而是在执行IO操作是,gevent自动切换,参考代码如下

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

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

gevent.joinall([
gevent.spawn(f, 'http://www.cnblogs.com/kaituorensheng/'),
gevent.spawn(f, 'https://www.python.org/'),
gevent.spawn(f, 'https://www.baidu.com'),
])

执行结果

GET: http://www.cnblogs.com/kaituorensheng/
GET: https://www.python.org/
GET: https://www.baidu.com
[227] bytes received from https://www.baidu.com

[14667] bytes received from http://www.cnblogs.com/kaituorensheng/

[47348] bytes received from https://www.python.org/

可以看到3个url结束顺序并不是依次执行完的。


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

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

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
Python NumPy库安装使用笔记
May 18 Python
Python出现segfault错误解决方法
Apr 16 Python
Python实现PS滤镜碎片特效功能示例
Jan 24 Python
Python实现的根据IP地址计算子网掩码位数功能示例
May 23 Python
Python叠加两幅栅格图像的实现方法
Jul 05 Python
python读写csv文件的方法
Aug 13 Python
python简单实现9宫格图片实例
Sep 03 Python
如何基于Python pygame实现动画跑马灯
Nov 18 Python
python爬虫中采集中遇到的问题整理
Nov 27 Python
python openssl模块安装及用法
Dec 06 Python
python 实现德洛内三角剖分的操作
Apr 22 Python
Pytorch中TensorBoard及torchsummary的使用详解
May 12 Python
利用Pandas和Numpy按时间戳将数据以Groupby方式分组
Jul 22 #Python
python+logging+yaml实现日志分割
Jul 22 #Python
python删除列表元素的三种方法(remove,pop,del)
Jul 22 #Python
python Gunicorn服务器使用方法详解
Jul 22 #Python
python实现按行分割文件
Jul 22 #Python
python UDP(udp)协议发送和接收的实例
Jul 22 #Python
linux环境下Django的安装配置详解
Jul 22 #Python
You might like
php获取文件类型和文件信息的方法
2015/07/10 PHP
javascript 数字格式化输出的实现代码
2013/12/10 Javascript
使用 TypeScript 重新编写的 JavaScript 坦克大战游戏代码
2015/04/07 Javascript
JS原型、原型链深入理解
2016/02/27 Javascript
JS控制静态页面传递参数并获取参数应用
2016/08/10 Javascript
微信小程序 扎金花简单实例
2017/02/21 Javascript
微信小程序CSS3动画下拉菜单效果
2018/11/04 Javascript
vue 2.5.1 源码学习 之Vue.extend 和 data的合并策略
2019/06/04 Javascript
在Koa.js中实现文件上传的接口功能
2019/10/08 Javascript
vue组件中节流函数的失效的原因和解决方法
2020/12/02 Vue.js
[04:44]DOTA2英雄梦之声_第12期_矮人直升机
2014/06/21 DOTA
Python 连连看连接算法
2008/11/22 Python
Python中最大最小赋值小技巧(分享)
2017/12/23 Python
Python3使用pandas模块读写excel操作示例
2018/07/03 Python
Sanic框架请求与响应实例分析
2018/07/16 Python
python中dict字典的查询键值对 遍历 排序 创建 访问 更新 删除基础操作方法
2018/09/13 Python
深入浅析Python2.x和3.x版本的主要区别
2018/11/30 Python
使用Python项目生成所有依赖包的清单方式
2020/07/13 Python
python实现数据结构中双向循环链表操作的示例
2020/10/09 Python
python 操作excel表格的方法
2020/12/05 Python
CSS3绘制不规则图形的一些方法示例
2015/11/07 HTML / CSS
澳大利亚连衣裙和女装在线:Esther
2017/11/11 全球购物
The North Face北面英国官网:美国著名户外品牌
2017/12/13 全球购物
Nordgreen英国官网:斯堪的纳维亚设计师手表
2018/10/24 全球购物
捷克玩具商店:Bambule
2019/02/23 全球购物
新西兰优惠网站:Treat Me
2019/07/04 全球购物
个人自我鉴定
2013/11/07 职场文书
建筑专业自荐信范文
2014/01/05 职场文书
公务员职业生涯规划书范文  
2014/01/19 职场文书
党员批评与自我批评
2014/02/12 职场文书
爱国卫生月活动总结范文
2014/04/25 职场文书
2014年租房协议书范本
2014/10/30 职场文书
考生诚信考试承诺书
2015/04/29 职场文书
烈士陵园观后感
2015/06/08 职场文书
学习经验交流会策划书
2015/11/02 职场文书
详细分析PHP7与PHP5区别
2021/06/26 PHP