python 2.7.3を実行していますが、次の奇妙な動作に気づきました。この最小限の例を考えてみてください。
_from multiprocessing import Process, Queue
def foo(qin, qout):
while True:
bar = qin.get()
if bar is None:
break
qout.put({'bar': bar})
if __name__ == '__main__':
import sys
qin = Queue()
qout = Queue()
worker = Process(target=foo,args=(qin,qout))
worker.start()
for i in range(100000):
print i
sys.stdout.flush()
qin.put(i**2)
qin.put(None)
worker.join()
_
1万回以上ループすると、スクリプトがworker.join()
でハングします。ループが1,000に達すると、正常に動作します。
何か案は?
サブプロセスのqout
キューがいっぱいになります。 foo()
からそこに入力したデータは、内部で使用されているOSのパイプのバッファーに収まらないため、サブプロセスはより多くのデータを収得しようとします。しかし、親プロセスはこのデータを読み取っていません。単にブロックされ、サブプロセスが完了するのを待っています。これは典型的なデッドロックです。
キューのサイズには制限が必要です。次の変更を検討してください。
_from multiprocessing import Process, Queue
def foo(qin,qout):
while True:
bar = qin.get()
if bar is None:
break
#qout.put({'bar':bar})
if __name__=='__main__':
import sys
qin=Queue()
qout=Queue() ## POSITION 1
for i in range(100):
#qout=Queue() ## POSITION 2
worker=Process(target=foo,args=(qin,))
worker.start()
for j in range(1000):
x=i*100+j
print x
sys.stdout.flush()
qin.put(x**2)
qin.put(None)
worker.join()
print 'Done!'
_
これは現状のまま機能します(_qout.put
_行はコメント化されています)。 100000件すべての結果を保存しようとすると、qout
が大きくなりすぎます。foo
のqout.put({'bar':bar})
のコメントを外して、POSITION 1のqout
の定義を残すと、コードがハングします。ただし、qout
定義をPOSITION 2に移動すると、スクリプトが終了します。
つまり、qin
もqout
も大きくなりすぎないように注意する必要があります。 (参照: マルチプロセッシングキューの最大サイズの制限は32767です )
合計サイズが約5000キューのキューに文字列を入れようとしたときに、python3
でも同じ問題が発生しました。
私のプロジェクトには、キューを設定してサブプロセスを開始してから参加するホストプロセスがありました。 Afrer join
ホストプロセスはキューから読み取ります。サブプロセスが生成するデータが多すぎる場合、ホストはjoin
でハングします。次の関数を使用してこれを修正し、ホストプロセスのサブプロセスを待機しました。
def yield_from_process(q, p):
while p.is_alive():
p.join(timeout=1)
while True:
try:
yield q.get(block=False)
except Empty:
break
キューがいっぱいになるとすぐに読み取るため、非常に大きくなることはありません
プールが閉じた後、非同期ワーカーを.get()
しようとしました
withブロック外のインデントエラー
私はこれを持っていました
with multiprocessing.Pool() as pool:
async_results = list()
for job in jobs:
async_results.append(
pool.apply_async(
_worker_func,
(job,),
)
)
# wrong
for async_result in async_results:
yield async_result.get()
私はこれが必要でした
with multiprocessing.Pool() as pool:
async_results = list()
for job in jobs:
async_results.append(
pool.apply_async(
_worker_func,
(job,),
)
)
# right
for async_result in async_results:
yield async_result.get()