gevent初探

Python已经通过生成器的yield和send方法给我们提供了实现协程的基础设施。

send方法可能比较冷门,send函数可以给一个生成器传递一个值,来启动yield。yield也可以同样作用于send。这样一来一回,就发生了程序的切换,也就是最基本的协程原理。

下面的demo可能更容易懂。

# -*- coding: utf-8 -*-
import time
def consumer():
    n = '200 ok'
    food = None
    while True:
        food = yield n
        print "eat food %d"%(food)  

def produce(c ,size):
    for i in range(size):
        n = c.send(i)
        print "produce food %d and receive msg %s"%(i,n)

def main():
    c = consumer()  #创建一个生层器
    c.next()       #启动生成器
    produce( c , 10)

if __name__ == '__main__':
    main()

但这也太TM糙了吧!这可不符合那些以造轮子来衡量自己水平大牛们的性格,于是就有了gevent这个神级库。
在接下来的阅读前,首先要明白,
1、gevent协程适合I/O密集,不适合CPU密集。
3、gevent协程无法发挥多核优势,事实上,协程只是以单线程的方式在运行。
3、子程序就是协程的一种特例

开胃菜

import gevent
import time

def work():
    gevent.sleep(1)   #做其他io操作而不是sleep

def asynchronous():
    threads = [ gevent.spawn( work) for i in range(5)]
    gevent.joinall( threads)

def synchronous():
    for i in range(5):
        work()

def main():
    start = time.time()
    asynchronous()
    end = time.time()
    print ("asynchronous spent %f"%(end-start))

    start = time.time()
    synchronous()
    end = time.time()
    print ("asynchronous spent %f"%(end-start))

if __name__ == '__main__':
    main()

通常的步骤

thread1 = gevent.spawn(woker)
thread2 = gevent.spawn(woker)  #创建路线线程
threads = [thread1, thread2]

gevent.joinall(threads)         #进行阻塞

获得Greenlet的状态
一个绿色线程有如下属性

  • started :Greenlet是否启动
  • ready: Greenlet 是否停止
  • value:Greenlet的返回值
  • successful: 是否已经停止且没有抛出异常
  • exception :获得抛出的异常

避免僵尸进程

gevent.signal(signal.SIGQUIT, gevent.shutdown)

主程序崩溃,就kill掉所有Greenlet。

猴子补丁
给Python标准库做修改来适应gevent,提高性能。

这里写图片描述

数据结构

事件(event)
类似yield的东西,更好用。用于Greenlet通信。

import gevent
from gevent.event import Event


evt = Event()

def produce():
    print('produce....')
    gevent.sleep(5)
    print('produce one!')
    evt.set()

def consume():
    print('waiting')
    evt.wait()
    print('consume one!')

def main():
    gevent.joinall([
        gevent.spawn(consume),
        gevent.spawn(consume),
        gevent.spawn(consume),
        gevent.spawn(produce)        
    ] )


if __name__ == '__main__':
    main()

Event对象不能传值,只能简单通知Greenlet。要传值的话用AsyncResult()对象。

import gevent
from gevent.event import Event,AsyncResult

evt = AsyncResult()

def produce():
    print('produce....')
    gevent.sleep(5)
    print('produce one!')
    evt.set("one cake")

def consume():
    print('waiting')
    something = evt.get()
    print('consume %s!'%(something))

def main():
    gevent.joinall([
        gevent.spawn(consume),
        gevent.spawn(consume),
        gevent.spawn(consume),
        gevent.spawn(produce)        
    ])

if __name__ == '__main__':
    main()

队列
Event只能简单的处理消息通知,上面的生产——消费者模型有问题,一个变量被共享了,这不是个好事情。所以有了队列。

import gevent
from gevent.queue import Queue
evt = Queue()

def produce():
    print('produce....')
    for i in range(1,4):
        print('produce one!')
        evt.put_nowait(i)
        gevent.sleep(i)

def consume():
    print('waiting')
    something = evt.get()
    print('consume %s!'%(something))

def main():
    gevent.joinall([
        gevent.spawn(consume),
        gevent.spawn(consume),
        gevent.spawn(consume),
        gevent.spawn(produce)        
    ])

if __name__ == '__main__':
    main()

get操作可以设置timeout=x,超过时间抛出gevent.queue.Empty。get(timeout=0)==get_nowait
Queue可以设置maxsize。如果队列长度等于maxsize,对于阻塞操作put会阻塞。put_nowait会抛出gevent.queue.Full.

组和池

import gevent
from gevent import getcurrent
from gevent.pool  import Group
import time
import random
def work(n):
    gevent.sleep(random.randint(1,3))
    return n

g = Group()

a=g.imap_unordered(work,range(10))
print 'unorder'
for i in a:
    print i,

print
a=g.imap(work,range(10))
print 'order'
for i in a:
    print i,

在学习group的时候让我蒙逼的是map和imap并没有指定大小组大小,后来看了源码,发现,map其实是imap的包装,唯一区别是imap返回迭代对象,map返回的是 list(map)。造成一个很大的区别是,imap是lazy的,也就是你不用它的计算结果,imap就不会去计算,而map要获得一个imap的list结果,于是是立即执行的。!

imap可以设置maxsize, map不行,所以map调用的是imap的根据情况设置的大小。
在计算过程中,imap 有有序和无序两种计算方式。 具体看上面的demo。无序的更轻量级,效率好一点。

那group和pool有什么区别呢?func
在我看来,其实没有本质区别!看源码的注释

A pool is like a group, but the maximum number of members is governed by the size parameter.

通过源码,发现pool其实是group的子类,唯一的差别是pool限制了Greenlets的数量。所以用法也差不多。

偷个懒,官网上写的demo,

from gevent.pool import Pool

class SocketPool(object):

    def __init__(self):
        self.pool = Pool(1000)
        self.pool.start()

    def listen(self, socket):
        while True:
            socket.recv()

    def add_handler(self, socket):
        if self.pool.full():
            raise Exception("At maximum pool size")
        else:
            self.pool.spawn(self.listen, socket)

    def shutdown(self):
        self.pool.kill()

锁和信号量
这是老生长谈的话题了,并行编程总会遇到竞争条件问题。毫无疑问,gevent提供了信号量。

from gevent.coros import BoundedSemaphore
sem = BoundedSemaphore(2)

BoundedSemaphore的参数表示允许多少个greenlet进入临界区,只有一个的时候就成为
了锁。
使用方法:

sem.acquire()
#condition race
sem.release()

或者担心忘记释放信号量这种低级错误,可以用with上下文管理器。

with sem:
    #condition race

参考: http://xlambda.com/gevent-tutorial/#_10

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值