該模塊提供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!”。
欲知后事如何,且聽下回分解。