为什么`gevent.spawn`与monkeypatched`threading.Thread()`不同?

Dav*_*ver 8 python multithreading gevent

虽然双重检查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()
Run Code Online (Sandbox Code Playgroud)

请注意,具体来说,两个注释开头XXX.

当使用第一行(with 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()

mou*_*uad 5

在您的代码中发生的事情是,您在列表中创建的greenletthreads还没有机会被执行,因为gevent在您使用gevent.sleep()这样或者通过调用函数隐式地在代码中明确地执行此操作之前不会触发上下文切换例如,semaphore.wait()或通过屈服等等...,看到你可以在之前插入一个打印cv.wait()并看到它只在cv.notify_all()被调用之后被调用:

def wait_on_cv(x):
    cv.acquire()
    print 'acquired ', x
    cv.wait()
    ....
Run Code Online (Sandbox Code Playgroud)

因此,对代码的简单修复就是在创建greenlet列表后插入会触发上下文切换的内容,例如:

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

注意:我还是新手,gevent所以我不知道这是不是正确的做法:)

这样所有的greenlets都有机会被执行,并且每个greenlet都会在他们调用时触发一个上下文切换,cv.wait()同时他们会将他们自己注册到条件服务员,这样当cv.notify_all()调用它时会通知所有的greenlets.

HTH,