複数のプロセス間で結果キューを共有する


92

multiprocessingモジュールのドキュメントには、で始まるプロセスにキューを渡す方法が示されていますmultiprocessing.Process。しかし、どのようにして非同期ワーカープロセスでキューを共有できapply_asyncますか?動的な結合などは必要ありません。ワーカーが(繰り返し)結果をベースに報告する方法にすぎません。

import multiprocessing
def worker(name, que):
    que.put("%d is done" % name)

if __name__ == '__main__':
    pool = multiprocessing.Pool(processes=3)
    q = multiprocessing.Queue()
    workers = pool.apply_async(worker, (33, q))

これは失敗します: RuntimeError: Queue objects should only be shared between processes through inheritance。私はこれが何を意味するのかを理解し、酸洗い/酸洗いを必要とするのではなく継承するためのアドバイス(およびWindowsのすべての特別な制限)を理解しています。しかし、機能するようにキューを渡すにはどうすればよいですか?私は例を見つけることができず、さまざまな方法で失敗した代替案をいくつか試しました。助けてください?

回答:


133

multiprocessing.Managerを使用してキューを管理し、別のワーカーがアクセスできるようにします。

import multiprocessing
def worker(name, que):
    que.put("%d is done" % name)

if __name__ == '__main__':
    pool = multiprocessing.Pool(processes=3)
    m = multiprocessing.Manager()
    q = m.Queue()
    workers = pool.apply_async(worker, (33, q))

ありがとうございます。元のコードの非同期呼び出しに無関係の問題があったため、修正をあなたの答えにもコピーしました。
アレクシス2012年

16
queue.Queue()これに適さない理由は何ですか?
mrgloom

@mrgloom:queue.Queueメモリ内ロックを使用して、スレッド化のために構築されました。マルチプロセス環境でqueue.Queue()は、サブプロセスはメモリを(ほとんど)共有しないため、各サブプロセスは独自のメモリ空間にインスタンスの独自のコピーを取得します。
LeoRochael

@alexis複数のワーカーがデータを挿入した後、Manager()。Queue()から要素を取得する方法は?
MSS

10

multiprocessing.Poolはすでに共有結果キューを持っているので、を追加で含める必要はありませんManager.QueueManager.Queueあるqueue.Queue別のサーバプロセスにあり、プロキシを介して露出し、フードの下(マルチスレッドキュー)。これにより、プールの内部キューに比べてオーバーヘッドが追加されます。Poolのネイティブの結果処理に依存するのとは逆に、の結果Manager.Queueも順序付けされるとは限りません。

ワーカープロセスはで開始されません.apply_async()これは、インスタンス化しPoolたときにすでに発生しています。何され、あなたが呼び出すときに開始することはpool.apply_async()、新たな「仕事」です。プールのワーカープロセスは、multiprocessing.pool.worker内部で- 関数を実行します。この関数は、プールの内部Pool._inqueueを介して転送された新しい「タスク」を処理し、を介して親に結果を送り返しPool._outqueueます。指定したfuncは内で実行されますmultiprocessing.pool.worker。何かがあるfuncだけreturnで、結果は自動的に親に送り返されます。

.apply_async() 即時(非同期)にAsyncResultオブジェクト(のエイリアス)を返しますApplyResult.get()実際の結果を受け取るには、そのオブジェクトを呼び出す(ブロックする)必要があります。別のオプションは、結果が準備ができるとすぐに発生するコールバック関数を登録することです。

from multiprocessing import Pool

def busy_foo(i):
    """Dummy function simulating cpu-bound work."""
    for _ in range(int(10e6)):  # do stuff
        pass
    return i

if __name__ == '__main__':

    with Pool(4) as pool:
        print(pool._outqueue)  # DEMO
        results = [pool.apply_async(busy_foo, (i,)) for i in range(10)]
        # `.apply_async()` immediately returns AsyncResult (ApplyResult) object
        print(results[0])  # DEMO
        results = [res.get() for res in results]
        print(f'result: {results}')       

出力例:

<multiprocessing.queues.SimpleQueue object at 0x7fa124fd67f0>
<multiprocessing.pool.ApplyResult object at 0x7fa12586da20>
result: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

注:timeout-parameterを指定しても.get()、ワーカー内のタスクの実際の処理は停止せず、を発生させて待機中の親のブロックを解除するだけmultiprocessing.TimeoutErrorです。


おもしろい、最初のチャンスを試してみます。それは確かに2012年にこのように動作しませんでした
アレクシス

@alexis Python 2.7(2010)に関連してここでは、コンテキストマネージャーとのerror_callback- パラメーターが欠落しているだけなapply_asyncので、それ以降あまり変更されていません。
Darkonaut
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.