2012-10-23 3 views
8

Durante il doppio controllo che la patch di threading.Condition sia corretta per la scimmia, ho notato che un threading.Thread(…).start() monkeypatched si comporta in modo diverso da gevent.spawn(…).Perché `gevent.spawn` è diverso da un` threading.Thread() `monkeypatched?

considerare:

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() 

nota, in particolare, i due commenti che iniziano con XXX.

Quando si utilizza la prima linea (con gevent.spawn), il primo thread.join() solleva un'eccezione:

 
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 

Tuttavia, Thread(…).start() (il secondo blocco), tutto funziona come previsto.

Perché dovrebbe essere? Qual è la differenza tra gevent.spawn() e Thread(…).start()?

risposta

5

Cosa succede nel codice è che i greenlets che avete creato in te threads lista non hanno avuto ancora la possibilità di essere eseguita perché gevent non si innescherà un cambio di contesto fino a che fare in modo esplicito nel codice utilizzando gevent.sleep() e tale o implicitamente chiamando una funzione che blocca ad es semaphore.wait() o cedendo e così via ..., per vedere che è possibile inserire una stampa prima di cv.wait() e vedere che si chiama solo dopo cv.notify_all() si chiama:

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

Quindi una soluzione semplice per il vostro codice sarà quello di inserire qualcosa che attiverà un cambio di contesto dopo aver creato l'elenco delle greenlets, ad esempio:

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

Nota: sono ancora nuovo per gevent quindi non so se questo è il modo giusto di fare :)

In questo modo tutti i greenlets avranno la possibilità di essere eseguito e ognuno di loro farà scattare un cambio di contesto quando chiamano cv.wait() e nel frattempo si registrarle auto alla condizione camerieri in modo che quando cv.notify_all() si chiama notificherà tutti i greenlets.

HTH,