Почему `gevent.икра "отличается от обезьяны".Thread()`?

при двойной проверке этого threading.Condition правильно патченные, я заметил, что monkeypatched threading.Thread(…).start() отличается от gevent.spawn(…).

считаем:

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

1 ответов


что происходит в вашем коде, так это то, что это что вы создали в вас threads список еще не имел возможности быть выполненным, потому что gevent не вызовет переключатель контекста, пока вы не сделаете это явно в своем коде, используя gevent.sleep() и такой или неявно, вызывая функцию, которая блокирует, например,semaphore.wait() или путем уступать и так далее ..., чтобы увидеть, что вы можете вставить печать до cv.wait() и смотрите, что он называется только после cv.notify_all() is крикнул:

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

таким образом, простым исправлением вашего кода будет вставка чего-то, что вызовет переключатель контекста после создания списка это, например:

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

Примечание: Я все еще новичок в gevent поэтому я не знаю, правильно ли это сделать :)

сюда все это будет иметь возможность быть выполненным, и каждый из них вызовет переключатель контекста при вызове cv.wait() и в то же время зарегистрируйте их самостоятельно к официантам условия так, что когда cv.notify_all() называется это уведомит всех это.

HTH,