8

在仔细检查threading.Condition猴子补丁是否正确时,我注意到猴子补丁的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

使用第一行 (with gevent.spawn) 时,第一行thread.join()会引发异常:

通知!
加入 0
回溯(最近一次通话最后):
  文件“foo.py”,第 30 行,在
    线程.join()
  文件“…/gevent/greenlet.py”,第 291 行,加入
    结果 = self.parent.switch()
  文件“…/gevent/hub.py”,第 381 行,在交换机中
    返回 greenlet.switch(self)
gevent.hub.LoopExit:此操作将永远阻塞

但是,Thread(…).start()(第二个块),一切都按预期工作。

为什么会这样?gevent.spawn()和有什么区别Thread(…).start()

4

1 回答 1

5

在您的代码中发生的情况是,您在列表中创建的greenletsthreads还没有机会执行,因为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都有机会被执行,并且每个greenlets在调用时都会触发上下文切换,cv.wait()同时他们会将自己注册到条件服务员,以便在cv.notify_all()调用时通知所有greenlets .

高温下,

于 2012-10-23T23:28:20.753 回答