Cette question a peut-être été posée dans un contexte similaire, mais je n’étais pas capable de trouver une réponse après environ 20 minutes de recherche. Je vais donc poser la question.
J'ai écrit un script Python (disons: scriptA.py) et un script (disons scriptB.py)
Dans scriptB, je veux appeler scriptA plusieurs fois avec des arguments différents. Chaque fois, il faut environ une heure pour s'exécuter (son script est énorme, fait beaucoup de choses .. ne vous inquiétez pas pour ça) et je veux pouvoir exécuter le scriptA avec tous les différents arguments simultanément, mais je dois attendre que tous soient terminés 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 exécuter tous les subprocess.call()
en même temps, puis attendre qu'ils soient tous terminés, comment dois-je faire cela?
J'ai essayé d'utiliser des threads comme dans l'exemple ici :
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 ce soit juste.
Comment savoir s'ils ont tous fini de s'exécuter avant de passer à mon do_finish()
?
Vous devez utiliser join method of Thread
object à 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
et t3
achèvent l'exécution.
Placez les discussions dans une liste, puis utilisez la méthode Join
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()
Je préfère utiliser la compréhension de liste basée sur une liste de saisie:
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]
En Python3, depuis Python 3.2, il existe une nouvelle approche pour atteindre le même résultat, que je préfère personnellement au processus traditionnel de création de thread/start/join, package concurrent.futures
: https://docs.python.org/3/library/ concurrent.futures.html
En utilisant 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 définissant le nombre maximal de travailleurs simultanés.
Vous pouvez avoir en classe quelque chose comme ci-dessous à partir duquel vous pouvez ajouter un nombre n de fonctions ou de scripts console_scrit à exécuter en mode parallèle, 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 __== '__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()
Peut-être quelque chose comme
for t in threading.enumerate():
if t.daemon:
t.join()
De la threading
documentation du module
Il y a un objet «thread principal»; cela correspond à l'initiale fil de contrôle dans le programme Python. Ce n'est pas un fil de démon.
Il est possible que des “objets de thread factices” soient créés . Ce sont des objets thread correspondant à des "threads extraterrestres", qui sont les threads de contrôle démarrés en dehors du module de threading, tels que directement à partir du code C. Les objets de thread factices ont une fonctionnalité limitée; ils sont toujours considérés comme vivants et démoniaques, et ne peuvent pas être
join()
ed . Ils ne sont jamais supprimés, car il est impossible de détecter le terminaison de threads extraterrestres.
Donc, pour attraper ces deux cas où vous n'êtes pas intéressé à garder une liste des threads que vous créez:
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
Après quoi:
>>> print(data)
[0, 4, 12, 40]
Je viens de rencontrer le même problème où je devais attendre tous les threads créés à l'aide de la boucle for.J'ai juste essayé le code suivant. Ce n'est peut-être pas la solution parfaite, mais je pensais que ce serait une solution simple. tester:
for t in threading.enumerate():
try:
t.join()
except RuntimeError as err:
if 'cannot join current thread' in err:
continue
else:
raise