web-dev-qa-db-ja.com

Pythonのプロセス間で多くのキューを共有する

multiprocessing.Manager()と、それを使用して共有オブジェクト、特にワーカー間で共有できるキューを作成する方法を知っています。 この質問この質問この質問 そして 私自身の質問の1つ もあります。

ただし、非常に多くのキューを定義する必要があり、各キューは特定のプロセスのペアをリンクしています。プロセスの各ペアとそのリンクキューが変数keyによって識別されるとします。

データを入れたり取得したりする必要があるときに、辞書を使用してキューにアクセスしたいと思います。私はこれを機能させることができません。私はいくつかのことを試しました。 multiprocessingmpとしてインポートされた場合:

マルチプロセッシングモジュール(_for key in all_keys: DICT[key] = mp.Queue_と呼びます)によってインポートされた構成ファイルで_multi.py_のようなdictを定義してもエラーは返されませんが、キュー_DICT[key]_はプロセス間で共有されません。キューの独自のコピーがあるように見えるため、通信は行われません。

プロセスを定義して開始するメインのマルチプロセッシング関数の先頭でDICTを定義しようとすると、次のようになります。

_DICT = mp.Manager().dict()    
for key in all_keys:
    DICT[key] = mp.Queue()
_

エラーが発生します

_RuntimeError: Queue objects should only be shared between processes through
 inheritance
_

に変更

_DICT = mp.Manager().dict()    
for key in all_keys:
    DICT[key] = mp.Manager().Queue()
_

すべてを悪化させるだけです。 main関数内ではなく、_multi.py_の先頭で同様の定義を試行すると、同様のエラーが返されます。

コード内で各キューに明示的に名前を付けることなく、プロセス間で多くのキューを共有する方法が必要です。何か案は?

編集

プログラムの基本的なスキーマは次のとおりです。

1-いくつかの変数を定義する最初のモジュールをロードし、multiをインポートし、multi.main()を起動し、モジュールロードとコード実行のカスケードを開始する別のモジュールをロードします。その間...

2- _multi.main_は次のようになります。

_def main():
    manager = mp.Manager()
    pool = mp.Pool()
    DICT2 = manager.dict()

    for key in all_keys:
        DICT2[key] = manager.Queue()
        proc_1 = pool.apply_async(targ1,(DICT1[key],) ) #DICT1 is defined in the config file
        proc_2 =  pool.apply_async(targ2,(DICT2[key], otherargs,) 
_

poolmanagerを使用するのではなく、次のプロセスも起動していました。

_mp.Process(target=targ1, args=(DICT[key],))
_

3-関数_targ1_は、メインプロセスから(keyでソートされた)入力データを取得します。結果を_DICT[key]_に渡して、_targ2_がその作業を実行できるようにすることを目的としています。これは機能していない部分です。 _targ1_ s、_targ2_ sなどは任意の数であるため、キューの数は任意です。

4-これらのプロセスのいくつかの結果は、さまざまな配列の束に送信されます/ pandas keyによってインデックスが付けられ、アクセスできるようにしたいデータフレーム任意のプロセスから、別のモジュールで起動されたプロセスも含まれます。この部分はまだ記述しておらず、別の質問になる可能性があります(上記の3の答えでも4がうまく解決される可能性があるため、ここで説明します)。

12
Wapiti

multiprocessing.Queue()を引数として渡して共有しようとしたときに、問題が発生したようです。代わりに 管理キュー を作成することでこれを回避できます:

import multiprocessing
manager = multiprocessing.Manager()
passable_queue = manager.Queue()

マネージャーを使用して作成する場合、キュー自体ではなく、プロキシを格納してキューに渡すため、ワーカーに渡すオブジェクトが処理する場合でもがコピーされた場合でも、同じ基になるデータ構造であるキューを指します。これは(概念的には)C/C++のポインターと非常によく似ています。この方法でキューを作成すると、ワーカープロセスを起動したときにキューを渡すことができます。

これでキューを渡すことができるので、辞書を管理する必要がなくなります。すべてのマッピングを格納する通常の辞書をメインに保持し、ワーカープロセスに必要なキューのみを提供するため、マッピングにアクセスする必要はありません。

この例をここに書きました。ワーカー間でオブジェクトを渡しているように見えるので、ここで行います。処理の2つの段階があり、データがmainの制御で開始および終了するとします。パイプラインのようにワーカーを接続するキューを作成する方法を見てください。ただし、ワーカーに必要なキューのみを与えることで、マッピングについて知る必要はありません。

import multiprocessing as mp

def stage1(q_in, q_out):

    q_out.put(q_in.get()+"Stage 1 did some work.\n")
    return

def stage2(q_in, q_out):

    q_out.put(q_in.get()+"Stage 2 did some work.\n")
    return

def main():

    pool = mp.Pool()
    manager = mp.Manager()

    # create managed queues
    q_main_to_s1 = manager.Queue()
    q_s1_to_s2 = manager.Queue()
    q_s2_to_main = manager.Queue()

    # launch workers, passing them the queues they need
    results_s1 = pool.apply_async(stage1, (q_main_to_s1, q_s1_to_s2))
    results_s2 = pool.apply_async(stage2, (q_s1_to_s2, q_s2_to_main))

    # Send a message into the pipeline
    q_main_to_s1.put("Main started the job.\n")

    # Wait for work to complete
    print(q_s2_to_main.get()+"Main finished the job.")

    pool.close()
    pool.join()

    return

if __name__ == "__main__":
    main()

コードは次の出力を生成します。

メインは仕事を始めました。
ステージ1はいくつかの作業を行いました。
ステージ2はいくつかの作業を行いました。
メインは仕事を終えました。

プログラムがどのように機能するのかまだよくわからないため、キューまたはAsyncResultsオブジェクトを辞書に格納する例は含めませんでした。しかし、キューを自由に渡すことができるようになったので、必要に応じてキュー/プロセスのマッピングを格納する辞書を作成できます。

実際、実際に複数のワーカー間にパイプラインを構築する場合は、mainの「ワーカー間」キューへの参照を保持する必要さえありません。キューを作成してワーカーに渡し、mainが使用するキューへの参照のみを保持します。本当に「任意の数」のキューがある場合は、古いキューをできるだけ早くガベージコレクションできるようにすることを強くお勧めします。

20
skrrgwasme