2012-10-23 42 views
8

雖然仔細檢查了threading.Condition是否正確修補了猴子,但我注意到monkeypatched threading.Thread(…).start()的行爲與gevent.spawn(…)的行爲不同。爲什麼`gevent.spawn`不同於monkeypatched`threading.Thread()`?

考慮:

from gevent import monkey; monkey.patch_all() 
from threading import Thread, Condition 
import gevent 

cv = Condition() 

def wait_on_cv(x): 
    cv.acquire() 
    cv.wait() 
    print "Here:", x 
    cv.release() 

# XXX: This code yields "This operation would block forever" when joining the first thread 
threads = [ gevent.spawn(wait_on_cv, x) for x in range(10) ] 

""" 
# XXX: This code, which seems semantically similar, works correctly 
threads = [ Thread(target=wait_on_cv, args=(x,)) for x in range(10) ] 
for t in threads: 
    t.start() 
""" 

cv.acquire() 
cv.notify_all() 
print "Notified!" 
cv.release() 

for x, thread in enumerate(threads): 
    print "Joining", x 
    thread.join() 

注意,特別是,這兩個意見開始XXX

當使用所述第一線(帶gevent.spawn),所述第一thread.join()引發一個例外:

 
Notified! 
Joining 0 
Traceback (most recent call last): 
    File "foo.py", line 30, in 
    thread.join() 
    File "…/gevent/greenlet.py", line 291, in join 
    result = self.parent.switch() 
    File "…/gevent/hub.py", line 381, in switch 
    return greenlet.switch(self) 
gevent.hub.LoopExit: This operation would block forever 

然而,Thread(…).start()(第二塊),一切都按預期工作。

這是爲什麼? gevent.spawn()Thread(…).start()有什麼區別?

回答

5

在你的代碼是什麼發生的是,greenlets你已經在你threads列表中創建並沒有被執行卻把機會,因爲直到你使用你的代碼明確地這樣做gevent不會觸發上下文切換gevent.sleep()等等或通過調用一個阻止例如semaphore.wait()或屈服等等......,一看就知道你可以cv.wait()之前插入打印,看到它被稱爲cv.notify_all()只調用後:

def wait_on_cv(x): 
    cv.acquire() 
    print 'acquired ', x 
    cv.wait() 
    .... 

所以,一個簡單的辦法,以你的代碼將是插入是你創建的greenlets名單之後,將觸發上下文切換,例如:

... 
threads = [ gevent.spawn(wait_on_cv, x) for x in range(10) ] 
gevent.sleep() # Trigger a context switch 
... 

注:我還是新gevent所以我不知道這是做正確的方式它:)

這樣,所有greenlets將要執行的機會,他們中的每一個,當他們打電話cv.wait(),並在平均時間,他們將 註冊它們自身的條件服務員會引發上下文切換,這樣,當cv.notify_all()叫做 將通知所有greenlets

HTH,