J'ai essayé de lire la documentation à l'adresse http://docs.python.org/dev/library/multiprocessing.html mais je suis toujours aux prises avec le multitraitement de la file d'attente, du pool et du verrouillage. Et pour l'instant, j'ai pu construire l'exemple ci-dessous.
En ce qui concerne Queue and Pool, je ne sais pas si j'ai bien compris le concept, alors corrigez-moi si je me trompe. Ce que j'essaie de faire est de traiter 2 demandes à la fois (la liste de données en contient 8 dans cet exemple), que dois-je utiliser? Pool pour créer 2 processus pouvant gérer deux files d'attente différentes (2 au maximum) ou devrais-je simplement utiliser Queue pour traiter 2 entrées à chaque fois? Le verrou serait pour imprimer les sorties correctement.
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 __== '__main__':
mp_handler(data)
La meilleure solution à votre problème consiste à utiliser un Pool
. Utiliser Queue
s et disposer d'une fonctionnalité distincte "d'alimentation en file d'attente" est probablement excessif.
Voici une version légèrement réarrangée de votre programme, cette fois avec seulement 2 processus insérée dans un Pool
. Je crois que c'est la solution la plus simple, avec des modifications minimes du code d'origine:
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 __== '__main__':
mp_handler()
Notez que la fonction mp_worker()
accepte désormais un seul argument (un tuple des deux arguments précédents) car la fonction map()
fragmente vos données d'entrée en sous-listes, chaque sous-liste étant donnée comme un argument unique à votre fonction de travail.
Sortie:
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
Edit selon le commentaire de @Thales ci-dessous:
Si vous voulez "un verrou pour chaque limite de pool" afin que vos processus s'exécutent par paires, procédez comme suit:
A en attente B en attente | A fait, B fait | C en attente, D en attente | C fait, D fait | ...
changez ensuite la fonction de gestionnaire pour lancer des pools (de 2 processus) pour chaque paire de données:
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))
Maintenant, votre sortie est:
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
Cela pourrait ne pas être lié à 100% à la question, mais lors de ma recherche d'un exemple d'utilisation du multitraitement avec une file d'attente, cela apparaît en premier sur Google.
Il s'agit d'un exemple de classe de base que vous pouvez instancier, mettre des éléments dans une file d'attente et attendre que la file d'attente soit terminée. C'est tout ce dont j'avais besoin.
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()
Voici mon goto personnel pour ce sujet:
Gist ici, (demandes de tirage bienvenus!): 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 __== '__main__':
main()
Pour tous ceux qui utilisent des éditeurs comme Komodo Edit (win10), ajoutez 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()
ou en première ligne à:
if __== '__main__':
sys.stdout.flush()
Cela aide à voir ce qui se passe pendant l'exécution du script. au lieu d'avoir à regarder la zone de ligne de commande noire.
Voici un exemple tiré de mon code (pour le pool threadé, mais changez simplement le nom de la classe et vous aurez un pool de processus):
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()
Fondamentalement:
pool = ThreadPoolExecutor(6)
crée un pool pour 6 threadspool.submit(execute_run, rp)
ajoute une tâche au pool, le premier arogument est une fonction appelée dans un thread/processus, le reste des arguments est passé à la fonction appelée.pool.join
attend que toutes les tâches soient terminées.