gevent 延时、定时、超时、io等待、动态添加任务

firefly提供的gtwisted值得参考

实现以下几个工具 (被调用的函数最好都不要有返回值)

1.延时调用 => 就是gtwisted那个定时器

from gevent import Greenlet
import gevent

class Delay:
    """延迟对象
    """
    
    def __init__(self, f, *args, **kw):
        self.f = f
        self.args = args
        self.kw = kw
    
    def call(self):
        return self.f(*self.args, **self.kw)
    
class DelayCall(Greenlet):
    """以一个微线程的方式实现一个延时调用
    example:
    def p(x):
        print x
    d = DelayCall(5, p, "xx")
    d.start() # 会执行 d._run
    """
    
    def __init__(self, seconds, f, *args, **kw):
        Greenlet.__init__(self)
        self.seconds = seconds
        self.delay = Delay(f, *args, **kw)
        
    def cancel(self):
        """取消延时调用
        """
        self.kill()
        
    def _run(self):
        gevent.sleep(self.seconds)
        return self.delay.call()

2.定时调用 => 延时调用 + 递归 # 如下面代码

注: 递归不靠谱, 容易栈满, 下面用循环

class LoopingCall(Greenlet):

    """以一个微线程的方式实现一个定时调用 example:
    def p(x):
        print x
    lc = LoopingCall(5, p, "xx")
    lc.start() # 会执行 d._run
    # some condition
    lc.cancel()
    """
    
    def __init__(self, seconds, f, *args, **kw):
        Greenlet.__init__(self)
        self.seconds = seconds
        self.delay = Delay(f, *args, **kw)
        
    def cancel(self):
        """取消定时调用
        """
        self.kill()
        
    def _run(self):
        while True:
            gevent.sleep(self.seconds)
            self.delay.call()

3.超时调用 

注: 这个和自带的 gevent.Timeout作用不同, 这个主要用在设置tcp接受数据超时:

=>gevent.Timeout: 一个greenlet运行时间超过规定时间则触发

=>这里实现的Timeout: 一个函数在规定时间未被调用则触发

实现原理: 延时调用 + 如果该函数到来则reset该延时调用(取消该延时调用,重启一个延时调用)

class Timeout(object):
    """example:
    def p(x):
        print x
    t = Timeout(4, p, "xx")
    # 如果在4s内没有调用t.reset, 则会触发p被调用
    """
    def __init__(self, seconds, cb, *args, *kw):
        """
        """
        self.seconds = seconds
        self.cb = cb
        self.args = args
        self.kw = kw
        self.dc = DelayCall(seconds, cb, *args, **kw)
        self.dc.start()
     
    def cancel(self):
        self.dc.cancel()
        
    def reset(self):
        """要在要进行超时设置的函数里调用, 也可以使用其它方式(如继承)
        """
        self.dc.cancel()
        self.dc = DelayCall(self.seconds, self.cb, *self.args, **self.kw)
        self.dc.start()

        继承的版本(类似twisted的TimeoutMixin)

class TimeoutMixin(object):
    """example:
    class Test(TimeoutMixin):
        
        def __init__(self):
            self.set_timeout(180)
            
        def timeout_connection(self):
            print "timeout..."
    # 如果在180s内没有调用self.reset,或者self.set_timeout, 则会触发timeout_connection被调用
    """
    
    def __init__(self):
        self.dc = None
        self.start_flag = 0
    
    def set_timeout(self, seconds):
        """可以重新设置超时时间"""
        if self.start_flag == 1:
            self.cancel()
        self.seconds = seconds
        self.dc = DelayCall(seconds, self.timeout_connection)
        self.dc.start()
        self.start_flag = 1
    
    def timeout_connection(self):
        raise NotImplementedError
     
    def cancel(self):
        self.dc.cancel()
        self.start_flag = 0
        
    def reset(self):
        """重置超时
        """
        assert self.start_flag == 0
        self.dc.cancel()
        self.dc = DelayCall(self.seconds, self.timeout_connection)
        self.dc.start()


4.io等待一个协程

只要有一个线程等待就行,就可以调用 gevent.spawn(func, param) 随时增加任务了(类似于主线程等待子线程结束)

import gevent

def run_forever():
    while True:
        gevent.sleep(600)

def test(x):
    gevent.sleep(2)
    print x
    
def run():
    gevent.joinall([gevent.spawn(run_forever)])  # 只需要joinall这个io等待的spawn就可以了
    # 或者如下
    # gthread = gevent.spawn(run_forever)
    # gthread.join()

if __name__ == "__main__":
    gevent.spawn(test, "xx") # 可以随时添加spawn协程, 
    run()                    # 有点像twisted的reactor.run

官网doc http://www.gevent.org/

gevent指南 http://xlambda.com/gevent-tutorial/ (这个因为版本问题有很多错误)

gevent和zmq http://zeromq.github.io/pyzmq/eventloop.html

gevent和zmq例子 https://github.com/zeromq/pyzmq/tree/master/examples/gevent

gevent tcp服务器 http://my.oschina.net/1123581321/blog/208312

gevent 任务的持续追加和执行 http://iyuan.iteye.com/blog/781168 | http://iyuan.iteye.com/blog/897539

gevent任务持续追加 -- 动态添加并发任务--Pool

下面仅作为学习Pool的参考

# -*- coding: utf-8 -*-

from gevent.pool import Pool

class SocketPool(object):

    def __init__(self):
        self.pool = Pool(1000)
        # self.pool.start()
        # 调用start了,并且有while循环recv,不用join了
        # 但是1.0版gevent,好像报错,要求start参数为greenlet,所以我改为了调用Pool.join

    def loop_process(self, sock, addr):
        print "xx", addr
        while True:
            print sock.recv(128)

    def add_handler(self, sock, addr):
        if self.pool.full():
            raise Exception("At maximum pool size")
        else:
            self.pool.spawn(self.loop_process, sock, addr)

    def shutdown(self):
        self.pool.kill()
        
if __name__ == "__main__":
    from gevent import socket
    
    sp = SocketPool()
    sock = socket.socket()
    sock.bind(("127.0.0.1", 8881))
    sock.listen(500)
    def conn():
        while True:
            conn, addr = sock.accept()
            sp.add_handler(conn, addr)
    sp.pool.spawn(conn)
    sp.pool.join()

每个coroutine是不可以有阻塞的IO的。有了阻塞的IO,整个进程就被block了 (所谓轻量级线程,其实还是在一个进程里,进程都被阻塞了,当然就不work了),当然其他的也不执行了。每个coroutine只能做轻量级的事情,快速的任务切换,如果有耗时的操作, 异步到单独的进程。 


你可能感兴趣的:(gevent)