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

具体的には、 で始まる 2 つのコメントに注意してくださいXXX

最初の行 ( を使用gevent.spawn)を使用すると、最初の行thread.join()で例外が発生します。

通知されました!
参加中 0
トレースバック (最新の呼び出しが最後):
  ファイル「foo.py」の 30 行目
    スレッド.結合()
  ファイル「…/gevent/greenlet.py」、291行目、結合
    結果 = self.parent.switch()
  ファイル「…/gevent/hub.py」、381行目、スイッチ
    greenlet.switch(self) を返す
gevent.hub.LoopExit: この操作は永久にブロックされます

ただし、Thread(…).start()(2 番目のブロック)、すべてが期待どおりに機能します。

これはなぜでしょうか?gevent.spawn()とはどう違いThread(…).start()ますか?

4

1 に答える 1

5

コードで何が起こるかというと、リストで作成したグリーンレットthreadsはまだ実行される機会がありません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慣れていないので、これが正しい方法かどうかわかりません:)

このようにして、すべてのグリーンレットが実行される可能性があり、それらのそれぞれが呼び出し時にコンテキストスイッチをトリガーし、cv.wait()その間に自分自身を条件ウェイターに登録して、cv.notify_all()呼び出されたときにすべてのグリーンレットに通知します.

HTH、

于 2012-10-23T23:28:20.753 に答える