46

別の Python スクリプトのコントローラーとして使用したい Python スクリプトがあります。私は 64 個のプロセッサを搭載したサーバーを持っているので、この 2 番目の Python スクリプトの最大 64 個の子プロセスを生成したいと考えています。子スクリプトは次のように呼び出されます。

$ python create_graphs.py --name=NAME

NAME は、XYZ、ABC、NYU などのようなものです。

私の親コントローラー スクリプトでは、リストから name 変数を取得します。

my_list = [ 'XYZ', 'ABC', 'NYU' ]

私の質問は、これらのプロセスを子として生成する最良の方法は何ですか? 一度に子の数を 64 に制限したいので、ステータス (子プロセスが終了したかどうか) を追跡して、世代全体を効率的に実行し続けることができるようにする必要があります。

subprocess パッケージの使用を検討しましたが、一度に 1 つの子しか生成しないため、拒否しました。ようやくマルチプロセッサ パッケージを見つけましたが、スレッド全体とサブプロセスのドキュメントに圧倒されたことを認めます。

現在、私のスクリプトはsubprocess.call、一度に 1 つの子のみをスポーンするように使用しており、次のようになっています。

#!/path/to/python
import subprocess, multiprocessing, Queue
from multiprocessing import Process

my_list = [ 'XYZ', 'ABC', 'NYU' ]

if __name__ == '__main__':
    processors = multiprocessing.cpu_count()

    for i in range(len(my_list)):
        if( i < processors ):
             cmd = ["python", "/path/to/create_graphs.py", "--name="+ my_list[i]]
             child = subprocess.call( cmd, shell=False )

一度に 64 人の子供を産み出したいのです。他のスタック オーバーフローの質問で、人々が Queue を使用しているのを見ましたが、それはパフォーマンス ヒットを生み出すように見えますか?

4

4 に答える 4

67

探しているのは、マルチプロセッシングのプロセス プールクラスです。

import multiprocessing
import subprocess

def work(cmd):
    return subprocess.call(cmd, shell=False)

if __name__ == '__main__':
    count = multiprocessing.cpu_count()
    pool = multiprocessing.Pool(processes=count)
    print pool.map(work, ['ls'] * count)

そして、分かりやすいように計算例を示します。以下は、N が CPU カウントである N プロセスで 10000 タスクを分割します。プロセス数として None を渡していることに注意してください。これにより、Pool クラスはプロセス数に cpu_count を使用します (参照) 。

import multiprocessing
import subprocess

def calculate(value):
    return value * 10

if __name__ == '__main__':
    pool = multiprocessing.Pool(None)
    tasks = range(10000)
    results = []
    r = pool.map_async(calculate, tasks, callback=results.append)
    r.wait() # Wait on the results
    print results
于 2009-05-19T20:26:13.217 に答える
1

サブプロセスを使用して独自のソリューションを展開するのではなく、間違いなくマルチプロセッシングを使用します。

于 2009-05-19T20:04:01.373 に答える
1

アプリケーションからデータを取得するつもりがない限り、キューは必要ないと思います(データが必要な場合は、とにかくデータベースに追加する方が簡単かもしれません)

しかし、サイズのためにこれを試してみてください:

create_graphs.py スクリプトの内容をすべて「create_graphs」という関数に入れます

import threading
from create_graphs import create_graphs

num_processes = 64
my_list = [ 'XYZ', 'ABC', 'NYU' ]

threads = []

# run until all the threads are done, and there is no data left
while threads or my_list:

    # if we aren't using all the processors AND there is still data left to
    # compute, then spawn another thread
    if (len(threads) < num_processes) and my_list:
        t = threading.Thread(target=create_graphs, args=[ my_list.pop() ])
        t.setDaemon(True)
        t.start()
        threads.append(t)

    # in the case that we have the maximum number of threads check if any of them
    # are done. (also do this when we run out of data, until all the threads are done)
    else:
        for thread in threads:
            if not thread.isAlive():
                threads.remove(thread)

これにより、プロセッサよりもスレッドが1つ少なくなることはわかっています。これはおそらく良いことです。プロセッサは、スレッド、ディスクI / O、およびコンピューターで発生するその他のことを管理します。最後のコアを使用する場合は、コアを 1 つ追加するだけです

編集: my_list の目的を誤解した可能性があります。my_listスレッドを追跡する必要はまったくありません(スレッドはすべてthreadsリスト内の項目によって参照されるため)。しかし、これはプロセスの入力を供給する優れた方法です-またはさらに良い方法: ジェネレーター関数を使用してください;)

my_listとの目的threads

my_list関数で処理する必要があるデータを保持するの
threadsは、現在実行中のスレッドの単なるリストです

while ループは、新しいスレッドを開始してデータを処理し、スレッドの実行が完了しているかどうかを確認するという 2 つのことを行います。

したがって、(a) 処理するデータが増えるか、(b) 実行が終了していないスレッドがある限り、実行を継続するようにプログラムする必要があります。両方のリストが空になると、評価されFalse、while ループが終了します。

于 2009-05-19T20:04:26.350 に答える