マルチプロセッシングキュー、プール、ロックを使用した非常に単純な例


91

http://docs.python.org/dev/library/multiprocessing.htmlにあるドキュメントを読み込もうとしましたが、まだマルチプロセッシングキュー、プール、ロックに苦労しています。そして今のところ、以下の例を構築することができました。

キューとプールについては、コンセプトが正しく理解されているかどうかわかりません。間違っている場合は修正してください。私が達成しようとしているのは、一度に2つのリクエストを処理することです(この例では、データリストには8つあります)。2つの異なるキュー(最大2つ)を処理できる2つのプロセスを作成するプール、またはQueueを使用して毎回2つの入力を処理する必要がありますか?ロックは、出力を正しく印刷することです。

import multiprocessing
import time

data = (['a', '2'], ['b', '4'], ['c', '6'], ['d', '8'],
        ['e', '1'], ['f', '3'], ['g', '5'], ['h', '7']
)


def mp_handler(var1):
    for indata in var1:
        p = multiprocessing.Process(target=mp_worker, args=(indata[0], indata[1]))
        p.start()


def mp_worker(inputs, the_time):
    print " Processs %s\tWaiting %s seconds" % (inputs, the_time)
    time.sleep(int(the_time))
    print " Process %s\tDONE" % inputs

if __name__ == '__main__':
    mp_handler(data)

回答:


129

あなたの問題に対する最善の解決策は、を利用することPoolです。を使用しQueue、個別の「キューフィード」機能を持つことは、おそらくやり過ぎです。

これは、プログラムを少し並べ替えたバージョンです。今回は、2つのプロセスのみがにまとめられていPoolます。元のコードへの変更を最小限に抑えて、これが最も簡単な方法だと思います。

import multiprocessing
import time

data = (
    ['a', '2'], ['b', '4'], ['c', '6'], ['d', '8'],
    ['e', '1'], ['f', '3'], ['g', '5'], ['h', '7']
)

def mp_worker((inputs, the_time)):
    print " Processs %s\tWaiting %s seconds" % (inputs, the_time)
    time.sleep(int(the_time))
    print " Process %s\tDONE" % inputs

def mp_handler():
    p = multiprocessing.Pool(2)
    p.map(mp_worker, data)

if __name__ == '__main__':
    mp_handler()

mp_worker()関数はmap()入力データをサブリストにチャンクアップするため、関数は単一の引数(前の2つの引数のタプル)を受け入れることに注意してください。各サブリストはワーカー関数の単一の引数として指定されます。

出力:

Processs a  Waiting 2 seconds
Processs b  Waiting 4 seconds
Process a   DONE
Processs c  Waiting 6 seconds
Process b   DONE
Processs d  Waiting 8 seconds
Process c   DONE
Processs e  Waiting 1 seconds
Process e   DONE
Processs f  Waiting 3 seconds
Process d   DONE
Processs g  Waiting 5 seconds
Process f   DONE
Processs h  Waiting 7 seconds
Process g   DONE
Process h   DONE

以下の@Thalesコメントに従って編集:

プロセスがタンデムペアで実行されるように「各プール制限のロック」が必要な場合は、次のようにします。

待っているB待っている| A完了、B完了| C待機中、D待機中| C完了、D完了| ...

次に、ハンドラー関数を変更して、データのペアごとに(2つのプロセスの)プールを起動します。

def mp_handler():
    subdata = zip(data[0::2], data[1::2])
    for task1, task2 in subdata:
        p = multiprocessing.Pool(2)
        p.map(mp_worker, (task1, task2))

今あなたの出力は:

 Processs a Waiting 2 seconds
 Processs b Waiting 4 seconds
 Process a  DONE
 Process b  DONE
 Processs c Waiting 6 seconds
 Processs d Waiting 8 seconds
 Process c  DONE
 Process d  DONE
 Processs e Waiting 1 seconds
 Processs f Waiting 3 seconds
 Process e  DONE
 Process f  DONE
 Processs g Waiting 5 seconds
 Processs h Waiting 7 seconds
 Process g  DONE
 Process h  DONE

それを行う方法の単純で直接的な例をありがとう、しかしどのようにして各プール制限にロックを適用できますか?つまり、コードを実行する場合、「A待機B待機| A完了、b完了| C待機、D待機| C完了、D完了」のようなものを見たい
thclpr

2
つまり、AとBの両方が完了するまで、Cを開始したくないのですか。
Velimir Mlaker 2014年

正確には、multiprocessing.Processを使用して実行できますが、プールを使用して実行する方法がわかりません
thclpr

ありがとうございました。意図したとおりに動作しますが、関数mp_handlerでは、var1ではなく変数データを参照しています:)
thclpr

さて、ありがとうございvar1ますdata。代わりにグローバルを参照して、完全に削除しました。
Velimir Mlaker 2014年

8

これは質問に完全に関連しているわけではないかもしれませんが、キューでマルチプロセッシングを使用する例を検索すると、これは最初にgoogleに表示されます。

これは、インスタンス化してキューにアイテムを配置し、キューが完了するまで待機できる基本的なサンプルクラスです。それだけで十分です。

from multiprocessing import JoinableQueue
from multiprocessing.context import Process


class Renderer:
    queue = None

    def __init__(self, nb_workers=2):
        self.queue = JoinableQueue()
        self.processes = [Process(target=self.upload) for i in range(nb_workers)]
        for p in self.processes:
            p.start()

    def render(self, item):
        self.queue.put(item)

    def upload(self):
        while True:
            item = self.queue.get()
            if item is None:
                break

            # process your item here

            self.queue.task_done()

    def terminate(self):
        """ wait until queue is empty and terminate processes """
        self.queue.join()
        for p in self.processes:
            p.terminate()

r = Renderer()
r.render(item1)
r.render(item2)
r.terminate()

2
何であるitem1item2?それらは、2つの異なるプロセスで実行される、ある種のタスクまたは機能ですか?
Zelphir Kaltstahl 2016年

2
はい、それらは並列に処理されるタスクまたは入力パラメーターです。
linqu

8

これがこのトピックの私の個人的なgotoです:

ここに要旨(プルリクエストを歓迎します!):https : //gist.github.com/thorsummoner/b5b1dfcff7e7fdd334ec

import multiprocessing
import sys

THREADS = 3

# Used to prevent multiple threads from mixing thier output
GLOBALLOCK = multiprocessing.Lock()


def func_worker(args):
    """This function will be called by each thread.
    This function can not be a class method.
    """
    # Expand list of args into named args.
    str1, str2 = args
    del args

    # Work
    # ...



    # Serial-only Portion
    GLOBALLOCK.acquire()
    print(str1)
    print(str2)
    GLOBALLOCK.release()


def main(argp=None):
    """Multiprocessing Spawn Example
    """
    # Create the number of threads you want
    pool = multiprocessing.Pool(THREADS)

    # Define two jobs, each with two args.
    func_args = [
        ('Hello', 'World',), 
        ('Goodbye', 'World',), 
    ]


    try:
        # Spawn up to 9999999 jobs, I think this is the maximum possible.
        # I do not know what happens if you exceed this.
        pool.map_async(func_worker, func_args).get(9999999)
    except KeyboardInterrupt:
        # Allow ^C to interrupt from any thread.
        sys.stdout.write('\033[0m')
        sys.stdout.write('User Interupt\n')
    pool.close()

if __name__ == '__main__':
    main()

1
.map_async()が.map()より優れているかどうかは正確にはわかりません。
ThorSummoner

3
の引数get()はタイムアウトであり、開始されるジョブの数とは関係ありません。
マタ

@mataそう、それはポーリングループで使用することを意図していますか?.get(timeout=1)?そして.get()、完全なリストを取得するだけでいいですか?
ThorSummoner 2017

はい、.get()すべての結果が利用可能になるまで無期限に待機し、結果のリストを返します。ポーリングループを使用して、気象結果が利用可能かどうかを確認できます。または、コールバック関数をmap_async()呼び出しで渡すことができます。コールバック関数は、結果が利用可能になると、すべての結果に対して呼び出されます。
マタ2017

2

Komodo Edit(win10)のようなエディターを使用するすべての人に、以下を追加sys.stdout.flush()します。

def mp_worker((inputs, the_time)):
    print " Process %s\tWaiting %s seconds" % (inputs, the_time)
    time.sleep(int(the_time))
    print " Process %s\tDONE" % inputs
    sys.stdout.flush()

または最初の行として:

    if __name__ == '__main__':
       sys.stdout.flush()

これは、スクリプトの実行中に何が起こっているかを確認するのに役立ちます。黒いコマンドラインボックスを見る代わりに。


1

これが私のコードの例です(スレッドプールの場合ですが、クラス名を変更するだけでプロセスプールが作成されます)。

def execute_run(rp): 
   ... do something 

pool = ThreadPoolExecutor(6)
for mat in TESTED_MATERIAL:
    for en in TESTED_ENERGIES:
        for ecut in TESTED_E_CUT:
            rp = RunParams(
                simulations, DEST_DIR,
                PARTICLE, mat, 960, 0.125, ecut, en
            )
            pool.submit(execute_run, rp)
pool.join()

基本的に:

  • pool = ThreadPoolExecutor(6) 6スレッドのプールを作成します
  • 次に、タスクをプールに追加するforがたくさんあります
  • pool.submit(execute_run, rp) プールにタスクを追加します。最初の引数はスレッド/プロセスで呼び出された関数で、残りの引数は呼び出された関数に渡されます。
  • pool.join すべてのタスクが完了するまで待機します。

2
を使用しているconcurrent.futuresが、OPはmultiprocessingPython 2.7 について質問していることに注意してください。
Tim Peters
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.