web-dev-qa-db-ja.com

python multiprocessing-大きなキューの結合でプロセスがハングする

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に達すると、正常に動作します。

何か案は?

25
user545424

サブプロセスのqoutキューがいっぱいになります。 foo()からそこに入力したデータは、内部で使用されているOSのパイプのバッファーに収まらないため、サブプロセスはより多くのデータを収得しようとします。しかし、親プロセスはこのデータを読み取っていません。単にブロックされ、サブプロセスが完了するのを待っています。これは典型的なデッドロックです。

35
Armin Rigo

キューのサイズには制限が必要です。次の変更を検討してください。

_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が大きくなりすぎます。fooqout.put({'bar':bar})のコメントを外して、POSITION 1のqoutの定義を残すと、コードがハングします。ただし、qout定義をPOSITION 2に移動すると、スクリプトが終了します。

つまり、qinqoutも大きくなりすぎないように注意する必要があります。 (参照: マルチプロセッシングキューの最大サイズの制限は32767です

4
amd

合計サイズが約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

キューがいっぱいになるとすぐに読み取るため、非常に大きくなることはありません

3
Rugnar

プールが閉じた後、非同期ワーカーを.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()
0
ThorSummoner