2012-10-23 11 views
8

Trong khi kiểm tra kỹ lưỡng rằng threading.Condition được vá đúng cách, tôi nhận thấy rằng một con khỉ threading.Thread(…).start() hoạt động khác với gevent.spawn(…).Tại sao `gevent.spawn` khác với` sreading.Thread() `của monkeypatched?

xem xét:

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

Lưu ý, đặc biệt, hai bình luận bắt đầu với XXX.

Khi sử dụng dòng đầu tiên (với gevent.spawn), các thread.join() đầu tiên đặt ra một ngoại lệ:

 
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 

Tuy nhiên, Thread(…).start() (khối thứ hai), mọi thứ hoạt động như mong đợi.

Tại sao điều này là? Sự khác biệt giữa gevent.spawn()Thread(…).start() là gì?

Trả lời

5

gì xảy ra trong mã của bạn là greenlets mà bạn đã tạo trong bạn threads danh sách không vẫn chưa có cơ hội để được thực thi vì gevent sẽ không kích hoạt chuyển đổi bối cảnh cho đến khi bạn làm như vậy một cách rõ ràng trong mã của bạn sử dụng gevent.sleep() và như vậy hoặc ngầm định bằng cách gọi một hàm chặn ví dụ semaphore.wait() hoặc bằng năng suất và vân vân ..., để thấy rằng bạn có thể chèn một bản in trước cv.wait() và thấy rằng nó được gọi là chỉ sau khi cv.notify_all() được gọi là:

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

Vì vậy, một sửa chữa dễ dàng để mã của bạn sẽ để chèn cái gì đó sẽ kích hoạt một chuyển ngữ cảnh sau khi bạn tạo danh sách các greenlets, ví dụ:

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

Lưu ý: tôi vẫn còn mới để gevent vì vậy tôi không biết nếu điều này là đúng cách để làm nó :)

Bằng cách này tất cả các greenlets sẽ có cơ hội để được thực thi và mỗi một trong số họ sẽ kích hoạt một chuyển ngữ cảnh khi họ gọi cv.wait() và trong thời gian trung bình họ sẽ đăng ký họ tự đến người phục vụ điều kiện để khi cv.notify_all() được gọi là sẽ thông báo cho tất cả các số greenlets.

HTH,