171 votes

python multithreading attendre que tous les threads soient terminés

Cette question a peut-être été posée dans un contexte similaire, mais je n'ai pas trouvé de réponse après environ 20 minutes de recherche, alors je vais la poser.

J'ai écrit un script Python (disons : scriptA.py) et un script (disons scriptB.py).

Dans le scriptB, je veux appeler le scriptA plusieurs fois avec différents arguments, chaque fois prend environ une heure à exécuter, (c'est un énorme script, fait beaucoup de choses ne vous inquiétez pas pour ça) et je veux être capable d'exécuter le scriptA avec tous les différents arguments simultanément, mais je dois attendre jusqu'à ce que TOUS soient faits avant de continuer ; mon code :

import subprocess

#setup
do_setup()

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

#finish
do_finish()

Je veux faire fonctionner tous les subprocess.call() en même temps, et ensuite attendre qu'ils soient tous terminés, comment dois-je faire ?

J'ai essayé d'utiliser l'enfilage comme dans l'exemple aquí :

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()

Mais je ne pense pas que cela soit juste.

Comment puis-je savoir qu'ils ont tous fini de courir avant d'aller dans mon do_finish() ?

231voto

Aaron Digulla Points 143830

Placez les fils dans une liste et utilisez ensuite la fonction Méthode d'assemblage

 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()

196voto

Maksim Skurydzin Points 4606

Vous devez utiliser rejoindre méthode de Thread à la fin du script.

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()

Ainsi, le thread principal attendra que t1 , t2 y t3 terminer l'exécution.

56voto

Roberto Points 2226

Dans Python3, depuis Python 3.2, il existe une nouvelle approche pour atteindre le même résultat, que je préfère personnellement à la traditionnelle création/démarrage/jonction de threads, package concurrent.futures : https://docs.python.org/3/library/concurrent.futures.html

Utilisation d'un ThreadPoolExecutor le code serait :

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')

La sortie du code précédent est quelque chose comme :

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

L'un des avantages est que vous pouvez contrôler le débit en fixant le nombre maximum de travailleurs simultanés.

37voto

Adam Matan Points 15690

Je préfère utiliser la compréhension de liste basée sur une liste d'entrée :

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]

7voto

PBD Points 21

Vous pouvez avoir une classe comme ci-dessous à partir de laquelle vous pouvez ajouter un nombre 'n' de fonctions ou de scripts de console que vous voulez exécuter en parallèle, puis démarrer l'exécution et attendre que tous les travaux soient terminés.

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 __name__ == '__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()

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X