Python——eventlet.event
该模块提供eventlet的事件支持,事件提供了跨 greenthread 的操作原语。
同一个事件对象既可以发出事件也可以接收(等待)事件,不同的协程共享这一个事件对象,就为不同协程之间基于事件的同步提供了可能。
class eventlet.event.Event
该类型抽象了以下事件:任意数量的协程可以等待其他一个协程发出的某一个事件。
事件类似于一个只能容纳一个对象的队列,但是有以下两个方面的区别:
1. 调用 send() 绝不会取消对当前greenthread的调度;
2. send() 只能被调用一次,想要再发一遍这个事件,那么不好意思,重新创建一个Event对象吧。
事件对于协程之间交流结果非常有用,同时也是 GreenThread.wait() 实现的基础。
例如:
>>> from eventlet import event >>> import eventlet >>> evt = event.Event() >>> def baz(b): ... evt.send(b + 1) ... >>> _ = eventlet.spawn_n(baz, 3) >>> evt.wait() 4
该类主要的方法有:
1. ready()
2. send(result=None, exc=None)
3. reset()
5. wait()
class eventlet.event.Event的方法
判断一个Event对象有没有发出过事件,如果调用 wait() 会立即返回一个事件结果,那么此处就返回真值。
该方法用来避免等待那些需要一段时间才会超时的事件。例如,你可以将一堆时间放到一个Python列表中,然后重复地遍历他们,这是就可以调用 ready() 直到其中的一个事件返回True,然后就可以立刻调用 wait() 来获取它了。
2. send(result=None, exc=None)
用 result 唤醒等待者,然后立刻返回给父对象。
例如:
>>> from eventlet import event >>> import eventlet >>> evt = event.Event() >>> def waiter(): ... print('about to wait') ... result = evt.wait() ... print('waited for {0}'.format(result)) >>> _ = eventlet.spawn(waiter) >>> eventlet.sleep(0) about to wait >>> evt.send('a') >>> eventlet.sleep(0) waited for a
一个event对象不能多次调用 send() 方法:
>>> evt.send('whoops') Traceback (most recent call last): ... AssertionError: Trying to re-send() an already-triggered event.
可以在多次 send() 方法之间调用 reset() 来重用Event对象。注意使用 reset() 重置后调用 ready() 方法将返回假值,然后同一个Event对象又可以调用 send() 方法了。
作用类似于 send() 方法,只不过向等待者发送的是一个异常。
该方法的参数和 raise 方法的参数完全相同,如果单个异常对象被传进来,它会在 wait() 方法被调用的时候重新抛出,生成一个新的堆栈轨迹。
例如:
>>> from eventlet import event >>> evt = event.Event() >>> evt.send_exception(RuntimeError()) >>> evt.wait() Traceback (most recent call last): File "<stdin>", line 1, in <module> File "eventlet/event.py", line 120, in wait current.throw(*self._exc) RuntimeError
如果需要完整地保留堆栈轨迹,必须传入整个 sys.exc_info() 元组。
>>> import sys >>> evt = event.Event() >>> try: ... raise RuntimeError() ... except RuntimeError: ... evt.send_exception(*sys.exc_info()) ... >>> evt.wait() Traceback (most recent call last): File "<stdin>", line 1, in <module> File "eventlet/event.py", line 120, in wait current.throw(*self._exc) File "<stdin>", line 2, in <module> RuntimeError
此时会在Event对象内部存储一个 traceback 对象,这可能会导致循环引用。详见 sys.exc_info() 的文档。
等待直到另一个协程调用 send() 。返回其他协程传递给 send() 方法的值。
例如:
>>> from eventlet import event >>> import eventlet >>> evt = event.Event() >>> def wait_on(): ... retval = evt.wait() ... print("waited for {0}".format(retval)) >>> _ = eventlet.spawn(wait_on) >>> evt.send('result') >>> eventlet.sleep(0) waited for result
最后的一句如果改为调用 wait() 方法的话,只要已经有一个协程已经发出过事件,此处会立即返回结果:
>>> evt.wait() 'result'
一个Event对象在 send() 以后,除非 reset() ,否则多次 wait() 也不会删除 send() 时发出的值,再结合如果不 reset() ,send() 只能调用一次,不难理解为什么这里说是“多个协程等待其他一个协程发出某一个事件”了。
自己写了一个小剧本,放在这里解释这个模块:
import time import eventlet from eventlet import event from eventlet import greenthread evt = event.Event() def Joker(): i = 0 while i < 3: print "Joker: Can anybody get me?" i += 1 time.sleep(1) print evt.wait() print "Fighting." time.sleep(3) return "Joker: Sorry, you got missed." def Batman(): evt.send("Bat man: I'm coming for you!") return "Bat man: I'm back, and your day is coming soon!" #Joker() gt1 = greenthread.spawn(Joker) gt2 = greenthread.spawn_after(5, Batman) print gt1.wait() time.sleep(1) print gt2.wait() time.sleep(2) print "To be continued."
哥谭镇上有一臭名昭著的恶霸,人称小丑Joker,他作恶多端,欺行霸市。但哥谭镇的警察都收受了他的贿赂,所以他得以逍遥法外,为害苍生。
小丑猖狂无边,觉得身边的人都太无聊:小混混为非作歹都没有创意,警察又太软弱。这个世界上只有一个人能够挑起他的好奇——蝙蝠侠。于是小丑不断挑衅蝙蝠侠的底线,带头作乱3年后,等着蝙蝠侠上门这个事件的发生。
蝙蝠侠深知小丑狡诈多变,虽对小丑极度憎恶,但老管家一直劝他 “Son, you have to be patient.”,于是潜心修炼。终于在5年后,蝙蝠侠复出下山,身形矫健,身手了得,发出事件 “I'm coming for you!”。
蝙蝠侠与小丑大战哥谭之巅,历时三年,双方战得难解难分,眼见小丑即将落败之际,使出阴险之计,诱骗蝙蝠侠保护平民,自己趁机遁逃。
蝙蝠侠回身再战,小丑早已不见踪影,留下余音笼罩在哥谭镇的上空:“Sorry, you got missed.” 蝙蝠侠环顾四周道 “Your day will come, since I am back!”。
欲知后事如何,且听下回分解。