web-dev-qa-db-ja.com

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

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

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

if __== '__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のすべての特別な制限)を要求するのではなく、継承するアドバイスを理解します。しかし、どのようにdo動作する方法でキューを渡しますか?例を見つけることができず、さまざまな方法で失敗したいくつかの代替案を試しました。助けてください?

73
alexis

multiprocessing.Manager を使用して、キューを管理し、さまざまなワーカーがアクセスできるようにしてください。

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

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

_multiprocessing.Pool_には既に共有の結果キューがあります。_Manager.Queue_を追加で含める必要はありません。 _Manager.Queue_は、フードの下にある _queue.Queue_ (マルチスレッドキュー)であり、別のサーバープロセスにあり、プロキシ経由で公開されます。これにより、プールの内部キューと比較して追加のオーバーヘッドが追加されます。 Poolのネイティブの結果処理に依存するのとは異なり、_Manager.Queue_の結果も順序付けられているとは限りません。

ワーカープロセスはnot.apply_async()で開始されます。これは、Poolをインスタンス化するときにすでに発生しています。 pool.apply_async()を呼び出すときに開始されるのはisが新しい「ジョブ」です。プールのワーカープロセスは、内部で_multiprocessing.pool.worker_-関数を実行します。この関数は、プールの内部_Pool._inqueue_を介して転送される新しい「タスク」を処理し、_Pool._outqueue_を介して親に結果を送り返します。指定したfuncは_multiprocessing.pool.worker_内で実行されます。 funcは何かをreturnするだけでよく、結果は自動的に親に送り返されます。

.apply_async()immediately(非同期) AsyncResult オブジェクト(ApplyResultのエイリアス)を返します。実際の結果を受け取るには、そのオブジェクトで.get()(ブロッキング中)を呼び出す必要があります。別のオプションは、 callback 関数を登録することです。これは、結果が準備が整うとすぐに起動されます。

_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 __== '__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]
_

注:.get()timeout-パラメーターを指定しても、ワーカー内のタスクの実際の処理は停止せず、_multiprocessing.TimeoutError_を上げることで待機中の親のブロックを解除するだけです。

3
Darkonaut