python gevent 协程

简介

  • 没有切换开销。因为子程序切换不是线程切换,而是由程序自身控制,没有线程切换的开销,因此执行效率高,
  • 不需要锁机制。因为只有一个线程,也不存在同时写变量冲突,在协程中控制共享资源不加锁,只需要判断状态就好了,所以执行效率比多线程高很多

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服务器,立刻就获得了数倍的性能提升。

时间: 2024-10-26 02:06:56

python gevent 协程的相关文章

python2.0_s12_day9_协程&amp;Gevent协程

Python之路,Day9 - 异步IO\数据库\队列\缓存 本节内容 Gevent协程 Select\Poll\Epoll异步IO与事件驱动 Python连接Mysql数据库操作 协程 1.协程,又称微线程,纤程.英文名Coroutine.一句话说明什么是协程:协程是一种用户态的轻量级线程.(操作系统跟不知道它存在),那你指定协程的实现原理是什么吗? 我们来聊聊协程的实现原理: 首先我们知道多个线程在一个单核CPU上进行并发,它的操作过程是,操作系统能调动的最小单位是线程,当操作系统触发多个线

python中协程

协程是python中除了进程和线程之外又一种能够实现多任务的方式,又称为微线程,纤程,它相比于线程需要的资源更少. 在python种协程是通过generator实现的.通过yield保存当前运行的状态然后切换到另一个协程执行.普通的生产者-消费这模式是一个线程写消息,一个线程才能读取消息,因此需要控制队列的写入与读取数据.而改用协程可以在生产者生产消息后直接通过yield跳转到消费者开始执行,执行完毕后在切换到生产者,如此反复,效率极高. 在图中,我们可以看出通过next的方法使得生成器中的任务

python之协程

协程,又称微线程,纤程.英文名Coroutine.协程是一种用户态的轻量级线程. 所谓用户态就是说协程是由用户来控制的,CPU不认识协程,协程是跑在线程中的. 协程拥有自己的寄存器上下文栈.协程调试切换时,将寄存器上下文栈保存到其他地方,在切回来时,恢复先前保存的寄存器上下文栈. 因此,协程能保留上一次调用时的状态(即所有局部状态的一个特定组合),每次过程重入时,就相当于进入上一次调用的状态,也就是进入上一次离开时所处逻辑流的位置. 线程切换时会将上下文和栈保存到CPU的寄存器中. 协程的标准定

Python与协程从Python2—Python3

协程,又称微线程.纤程,英文名Coroutine:用一句话说明什么是线程的话:协程是一种用户态的轻量级线程. Python对于协程的支持在python2中还比较简单,但是也有可以使用的第三方库,在python3中开始全面支持,也成为python3的一个核心功能,很值得学习. 协程介绍 协程,又称微线程.纤程,英文名Coroutine:用一句话说明什么是线程的话:协程是一种用户态的轻量级线程. 协程拥有自己的寄存器上下文和栈.协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先

深入理解Python中协程的应用机制: 使用纯Python来实现一个操作系统吧!!

本文参考:http://www.dabeaz.com/coroutines/   作者:David Beazley 缘起: 本人最近在学习python的协程.偶然发现了David Beazley的coroutine课程,花了几天时间读完后,为了加深理解就把其中个人认为最为精华的部分摘下来并加上个人理解写了本篇博客. 扯一些淡: 既然要搞一个操作系统,那我们就先来设一个目标吧!就像找女朋友,我们不可能随随便便的是个女的就上,肯定要对女方有一定的要求,比如肤白貌美气质佳…… 所以,我们对这个' 姑娘

【Python】协程

协程,又称微线程,纤程.英文名Coroutine. 协程的概念很早就提出来了,但直到最近几年才在某些语言(如Lua)中得到广泛应用. 子程序,或者称为函数,在所有语言中都是层级调用,比如A调用B,B在执行过程中又调用了C,C执行完毕返回,B执行完毕返回,最后是A执行完毕. 所以子程序调用是通过栈实现的,一个线程就是执行一个子程序. 子程序调用总是一个入口,一次返回,调用顺序是明确的.而协程的调用和子程序不同. 协程看上去也是子程序,但执行过程中,在子程序内部可中断,然后转而执行别的子程序,在适当

Python之协程函数

Python之协程函数 什么是协程函数:如果一个函数内部yield的使用方法是表达式形式的话,如x=yield,那么该函数成为协程函数. def eater(name): print('%s start to eat food' %name) food_list=[] while True: food=yield food_list print('%s get %s ,to start eat' %(name,food)) food_list.append(food) print('done')

关于Python的协程问题总结

协程其实就是可以由程序自主控制的线程 在python里主要由yield 和yield from 控制,可以通过生成者消费者例子来理解协程 利用yield from 向生成器(协程)传送数据# 传统的生产者-消费者是一个线程写消息,一个线程取消息,通过锁机制控制队列和等待,但一不小心就可能死锁.# 如果改用协程,生产者生产消息后,直接通过yield跳转到消费者开始执行,待消费者执行完毕后,换回生产者继续生产,效率极高 def consumer(): r = '' while True: n = y

【python】gevent协程例子

说在前面:用协程还是多线程需要仔细考量.我在做实验时请求了100w个ip,分别用pool为1000的协程和64个线程来跑,结果是多线程的速度是协程的10倍以上. 一个简单的协程例子 #!/usr/bin/env python #coding=utf-8 from gevent import monkey; monkey.patch_socket() import gevent.pool import gevent def do_something(ip): #在io时协程自动切换 #reques