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只能做轻量级的事情,快速的任务切换,如果有耗时的操作, 异步到单独的进程。