web-dev-qa-db-ja.com

pythonマルチスレッドは、すべてのスレッドが終了するまで待機します

これは同様の文脈で尋ねられたかもしれませんが、約20分間の検索で答えを見つけることができなかったので、尋ねます。

Pythonスクリプト(scriptA.pyと言う)とスクリプト(scriptB.pyと言う)を作成しました

ScriptBでは、異なる引数を使用してscriptAを複数回呼び出し、そのたびに実行に約1時間かかります(その巨大なスクリプトで、多くのことを行います。心配する必要はありません)。 scriptAにすべての異なる引数を同時に指定しますが、続行する前にすべての引数が完了するまで待つ必要があります。私のコード:

import subprocess

#setup
do_setup()

#run scriptA
subprocess.call(scriptA + argumentsA)
subprocess.call(scriptA + argumentsB)
subprocess.call(scriptA + argumentsC)

#finish
do_finish()

すべてのsubprocess.call()を同時に実行し、すべてが完了するまで待ちたいのですが、どうすればよいですか?

例のようにスレッドを使用しようとしました here

from threading import Thread
import subprocess

def call_script(args)
    subprocess.call(args)

#run scriptA   
t1 = Thread(target=call_script, args=(scriptA + argumentsA))
t2 = Thread(target=call_script, args=(scriptA + argumentsB))
t3 = Thread(target=call_script, args=(scriptA + argumentsC))
t1.start()
t2.start()
t3.start()

しかし、私はこれが正しいとは思わない。

do_finish()に移動する前に、すべての実行が完了したことをどのようにして確認できますか?

92
Inbar Rose

スクリプトの最後でThreadオブジェクトの join メソッドを使用する必要があります。

t1 = Thread(target=call_script, args=(scriptA + argumentsA))
t2 = Thread(target=call_script, args=(scriptA + argumentsB))
t3 = Thread(target=call_script, args=(scriptA + argumentsC))

t1.start()
t2.start()
t3.start()

t1.join()
t2.join()
t3.join()

したがって、メインスレッドは、t1t2、およびt3の実行が完了するまで待機します。

125

スレッドをリストに入れてから Join method を使用します

 threads = []

 t = Thread(...)
 threads.append(t)

 ...repeat as often as necessary...

 # Start all threads
 for x in threads:
     x.start()

 # Wait for all of them to finish
 for x in threads:
     x.join()
147
Aaron Digulla

入力リストに基づいたリスト内包表記の使用を好みます:

inputs = [scriptA + argumentsA, scriptA + argumentsB, ...]
threads = [Thread(target=call_script, args=(i)) for i in inputs]
[t.start() for t in threads]
[t.join() for t in threads]
23
Adam Matan

Python3では、Python 3.2以降、同じ結果に到達するための新しいアプローチがあり、個人的には従来のスレッドの作成/開始/結合、パッケージconcurrent.futureshttps:// docs。 python.org/3/library/concurrent.futures.html

ThreadPoolExecutorを使用すると、コードは次のようになります。

from concurrent.futures.thread import ThreadPoolExecutor
import time

def call_script(ordinal, arg):
    print('Thread', ordinal, 'argument:', arg)
    time.sleep(2)
    print('Thread', ordinal, 'Finished')

args = ['argumentsA', 'argumentsB', 'argumentsC']

with ThreadPoolExecutor(max_workers=2) as executor:
    ordinal = 1
    for arg in args:
        executor.submit(call_script, ordinal, arg)
        ordinal += 1
print('All tasks has been finished')

前のコードの出力は次のようなものです。

Thread 1 argument: argumentsA
Thread 2 argument: argumentsB
Thread 1 Finished
Thread 2 Finished
Thread 3 argument: argumentsC
Thread 3 Finished
All tasks has been finished

利点の1つは、最大同時ワーカー数を設定するスループットを制御できることです。

20
Roberto

以下のようなクラスを使用して、並列情熱で実行する「n」個の関数またはconsole_scriptsを追加し、実行を開始してすべてのジョブが完了するのを待つことができます。

from multiprocessing import Process

class ProcessParallel(object):
    """
    To Process the  functions parallely

    """    
    def __init__(self, *jobs):
        """
        """
        self.jobs = jobs
        self.processes = []

    def fork_processes(self):
        """
        Creates the process objects for given function deligates
        """
        for job in self.jobs:
            proc  = Process(target=job)
            self.processes.append(proc)

    def start_all(self):
        """
        Starts the functions process all together.
        """
        for proc in self.processes:
            proc.start()

    def join_all(self):
        """
        Waits untill all the functions executed.
        """
        for proc in self.processes:
            proc.join()


def two_sum(a=2, b=2):
    return a + b

def multiply(a=2, b=2):
    return a * b


#How to run:
if __== '__main__':
    #note: two_sum, multiply can be replace with any python console scripts which
    #you wanted to run parallel..
    procs =  ProcessParallel(two_sum, multiply)
    #Add all the process in list
    procs.fork_processes()
    #starts  process execution 
    procs.start_all()
    #wait until all the process got executed
    procs.join_all()
5
PBD

threadingから モジュールのドキュメント

「メインスレッド」オブジェクトがあります。これは、Pythonプログラムの制御の初期スレッドに対応します。デーモンスレッドではありません。

「ダミースレッドオブジェクト」が作成される可能性があります。これらは、「エイリアンスレッド」に対応するスレッドオブジェクトです。これは、Cコードから直接など、スレッドモジュールの外部で開始される制御のスレッドです。ダミースレッドオブジェクトの機能は制限されています。それらは常に生きていてデーモンであると見なされ、join()edにすることはできません。エイリアンスレッドの終了を検出することは不可能なので、削除されません。

したがって、作成するスレッドのリストを保持することに興味がない場合に、これらの2つのケースをキャッチするには:

import threading as thrd


def alter_data(data, index):
    data[index] *= 2


data = [0, 2, 6, 20]

for i, value in enumerate(data):
    thrd.Thread(target=alter_data, args=[data, i]).start()

for thread in thrd.enumerate():
    if thread.daemon:
        continue
    try:
        thread.join()
    except RuntimeError as err:
        if 'cannot join current thread' in err.args[0]:
            # catchs main thread
            continue
        else:
            raise

すぐに:

>>> print(data)
[0, 4, 12, 40]
2
berna1111

たぶん、次のようなもの

for t in threading.enumerate():
    if t.daemon:
        t.join()
2
jno

Forループを使用して作成されたすべてのスレッドを待つ必要があるという同じ問題に遭遇しました。次のコードを試してみました。完璧な解決策ではないかもしれませんが、単純な解決策だと思いましたテストする:

for t in threading.enumerate():
    try:
        t.join()
    except RuntimeError as err:
        if 'cannot join current thread' in err:
            continue
        else:
            raise
1
Omkar