web-dev-qa-db-ja.com

Python multiprocessing.Queue vs multiprocessing.manager()。Queue()

そのような単純なタスクがあります:

def worker(queue):
    while True:
        try:
            _ = queue.get_nowait()
        except Queue.Empty:
            break

if __name__ == '__main__':
    manager = multiprocessing.Manager()
    # queue = multiprocessing.Queue()
    queue = manager.Queue()

    for i in range(5):
        queue.put(i)

    processes = []

    for i in range(2):
        proc = multiprocessing.Process(target=worker, args=(queue,))
        processes.append(proc)
        proc.start()

    for proc in processes:
        proc.join()

Multiprocessing.Queueは必要なすべての作業を実行できるように見えますが、その一方でmanager()。Queue()の多くの例を見て、本当に必要なものを理解できません。 Manager()。Queue()は何らかのプロキシオブジェクトを使用しているように見えますが、multiprocessing.Queue()はプロキシオブジェクトなしで同じ処理を行うため、これらの目的はわかりません。

だから、私の質問は:

1)multiprocessing.Queueとmultiprocessing.manager()。Queue()によって返されるオブジェクトの実際の違いは何ですか?

2)何を使用する必要がありますか?

31
novicef

このテーマについての私の理解は限られていますが、私がしたことからわかるように、multiprocessing.Queue()とmultiprocessing.Manager()。Queue()の間には1つの主な違いがあります。

  • multiprocessing.Queue()はオブジェクトですが、multiprocessing.Manager()。Queue()はmultiprocessing.Manager()オブジェクトによって管理される共有キューを指すアドレス(プロキシ)です。
  • したがって、通常のmultiprocessing.Queue()オブジェクトは、Pickできないため、Poolメソッドに渡すことはできません。
  • さらに、 python doc は、multiprocessing.Queue()を使用するときに特に注意を払うように指示します。

オブジェクトがキューに置かれると、オブジェクトはピクルされ、バックグラウンドスレッドは後でピクルされたデータを基になるパイプにフラッシュします。これは少し驚くべき結果をもたらしますが、実際的な問題を引き起こすことはありません。実際に問題が発生する場合は、代わりにマネージャーで作成されたキューを使用できます。オブジェクトを空のキューに置いた後、キューのempty()メソッドがFalseを返し、get_nowait()がQueue.Emptyを上げることなく戻ることができるまでに、わずかな遅延がある場合があります。複数のプロセスがオブジェクトをエンキューしている場合、オブジェクトが反対側で順不同で受信される可能性があります。ただし、同じプロセスによってキューに入れられたオブジェクトは、常に互いに対して期待される順序になります。

Warning上記のように、子プロセスがキューにアイテムを置いた場合(そしてJoinableQueue.cancel_join_threadを使用しなかった場合)、そのプロセスは終了しませんバッファされたすべてのアイテムがパイプにフラッシュされるまで。つまり、そのプロセスに参加しようとすると、キューに入れられたすべてのアイテムが消費されていることが確実でない限り、デッドロックが発生する可能性があります。同様に、子プロセスが非デーモンである場合、親プロセスはすべての非デーモンの子に参加しようとすると、終了時にハングする場合があります。マネージャーを使用して作成されたキューには、この問題はありません。

キューをグローバル変数として設定し、初期化時にすべてのプロセスに対して設定することにより、poolでmultiprocessing.Queue()を使用する回避策があります。

queue = multiprocessing.Queue()
def initialize_shared(q):
    global queue
    queue=q

pool= Pool(nb_process,initializer=initialize_shared, initargs(queue,))

正しく共有されたキューを使用してプールプロセスを作成しますが、multiprocessing.Queue()オブジェクトはこの使用のために作成されたものではないと主張できます。

一方、manager.Queue()は、関数の通常の引数として渡すことにより、プールサブプロセス間で共有できます。

私の意見では、multiprocessing.Manager()。Queue()を使用することはすべてのケースで問題なく、面倒ではありません。マネージャーの使用にはいくつかの欠点があるかもしれませんが、私はそれを知りません。

25
michael