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