简单了解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的Flask框架中实现简单的登录功能的教程
Apr 20 Python
django接入新浪微博OAuth的方法
Jun 29 Python
Python 记录日志的灵活性和可配置性介绍
Feb 27 Python
python try except返回异常的信息字符串代码实例
Aug 15 Python
Python FFT合成波形的实例
Dec 04 Python
基于python traceback实现异常的获取与处理
Dec 13 Python
pytorch之inception_v3的实现案例
Jan 06 Python
python 正则表达式参数替换实例详解
Jan 17 Python
django表单中的按钮获取数据的实例分析
Jul 31 Python
【超详细】八大排序算法的各项比较以及各自特点
Mar 31 Python
python入门学习关于for else的特殊特性讲解
Nov 20 Python
Python中tqdm的使用和例子
Sep 23 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
多重?l件?合查?(一)
2006/10/09 PHP
php时间函数用法分析
2016/05/28 PHP
用php实现分页效果的示例代码
2020/12/10 PHP
window.addEventListener来解决让一个js事件执行多个函数
2012/12/26 Javascript
jquery获取URL中参数解决中文乱码问题的两种方法
2013/12/18 Javascript
jQuery 重复加载错误以及修复方法
2014/12/16 Javascript
js使用setTimeout实现定时炸弹的方法
2015/04/10 Javascript
JavaScript获取两个数组交集的方法
2015/06/09 Javascript
jQuery移动web开发中的页面初始化与加载事件
2015/12/03 Javascript
jQuery实现下拉框左右移动(全部移动,已选移动)
2016/04/15 Javascript
微信小程序 网络请求(post请求,get请求)
2017/01/17 Javascript
ReactJs设置css样式的方法
2017/06/08 Javascript
Angular2 http jsonp的实例详解
2017/08/31 Javascript
vue-awesome-swiper滑块插件使用方法详解
2017/11/27 Javascript
Vue单页应用引用单独的样式文件的两种方式
2018/03/30 Javascript
JS常见DOM节点操作示例【创建 ,插入,删除,复制,查找】
2018/05/14 Javascript
微信运维交互机器人的示例代码
2018/11/12 Javascript
jQuery子选择器与可见性选择器实例分析
2019/06/28 jQuery
在Vue环境下利用worker运行interval计时器的步骤
2019/08/01 Javascript
JavaScript进阶(四)原型与原型链用法实例分析
2020/05/09 Javascript
[02:36]DOTA2上海特锦赛 回忆电竞生涯的重要瞬间
2016/03/25 DOTA
Python GAE、Django导出Excel的方法
2008/11/24 Python
零基础写python爬虫之爬虫的定义及URL构成
2014/11/04 Python
python获得两个数组交集、并集、差集的方法
2015/03/27 Python
Python贪心算法实例小结
2018/04/22 Python
解决python中使用plot画图,图不显示的问题
2018/07/04 Python
python3+PyQt5 使用三种不同的简便项窗口部件显示数据的方法
2019/06/17 Python
python写程序统计词频的方法
2019/07/29 Python
wxpython布局的实现方法
2019/11/01 Python
Python astype(np.float)函数使用方法解析
2020/06/08 Python
Python从文件中读取数据的方法步骤
2020/11/18 Python
重阳节标语大全
2014/10/07 职场文书
群众路线教育实践活动心得体会(教师)
2014/10/31 职场文书
欧也妮葛朗台读书笔记
2015/06/30 职场文书
Linux系统下安装PHP7.3版本
2021/06/26 PHP
pandas时间序列之pd.to_datetime()的实现
2022/06/16 Python