La documentation du module multiprocessing
montre comment passer une file d'attente à un processus démarré avec multiprocessing.Process
. Mais comment puis-je partager une file d'attente avec des processus de travail asynchrones commencés par apply_async
? Je n'ai pas besoin d'une jonction dynamique ou d'autre chose, juste un moyen pour les travailleurs de rapporter (à plusieurs reprises) leurs résultats à la base.
import multiprocessing
def worker(name, que):
que.put("%d is done" % name)
if __== '__main__':
pool = multiprocessing.Pool(processes=3)
q = multiprocessing.Queue()
workers = pool.apply_async(worker, (33, q))
Cela échoue avec: RuntimeError: Queue objects should only be shared between processes through inheritance
. Je comprends ce que cela signifie et je comprends les conseils d'hériter plutôt que d'exiger le décapage/décapage (et toutes les restrictions spéciales de Windows). Mais comment faire Je passe la file d'attente d'une manière qui fonctionne? Je ne trouve pas d'exemple et j'ai essayé plusieurs alternatives qui ont échoué de diverses manières. Aidez-moi, s'il vous plaît?
Essayez d'utiliser multiprocessing.Manager pour gérer votre file d'attente et la rendre également accessible à différents travailleurs.
import multiprocessing
def worker(name, que):
que.put("%d is done" % name)
if __== '__main__':
pool = multiprocessing.Pool(processes=3)
m = multiprocessing.Manager()
q = m.Queue()
workers = pool.apply_async(worker, (33, q))
multiprocessing.Pool
A déjà une file d'attente de résultats partagée, il n'est pas nécessaire d'impliquer en plus un Manager.Queue
. Manager.Queue
Est un queue.Queue
(file d'attente multithreading) sous le capot, situé sur un processus serveur distinct et exposé via des proxys. Cela ajoute une surcharge supplémentaire par rapport à la file d'attente interne de Pool. Contrairement à la gestion native des résultats de Pool, les résultats dans le Manager.Queue
Ne sont pas non plus garantis pour être commandés.
Les processus de travail ne sont pas pas démarrés avec .apply_async()
, cela se produit déjà lorsque vous instanciez Pool
. Ce que est démarré lorsque vous appelez pool.apply_async()
est un nouveau "travail". Les processus de travail de Pool exécutent la fonction multiprocessing.pool.worker
- sous le capot. Cette fonction prend en charge le traitement des nouvelles "tâches" transférées sur le Pool._inqueue
Interne du Pool et l'envoi des résultats au parent via le Pool._outqueue
. Votre func
spécifié sera exécuté dans multiprocessing.pool.worker
. func
n'a qu'à return
quelque chose et le résultat sera automatiquement renvoyé au parent.
.apply_async()
immédiatement (de manière asynchrone) renvoie un objet AsyncResult
(alias pour ApplyResult
). Vous devez appeler .get()
(bloque) sur cet objet pour recevoir le résultat réel. Une autre option serait d'enregistrer une fonction callback , qui est déclenchée dès que le résultat est prêt.
from multiprocessing import Pool
def busy_foo(i):
"""Dummy function simulating cpu-bound work."""
for _ in range(int(10e6)): # do stuff
pass
return i
if __== '__main__':
with Pool(4) as pool:
print(pool._outqueue) # DEMO
results = [pool.apply_async(busy_foo, (i,)) for i in range(10)]
# `.apply_async()` immediately returns AsyncResult (ApplyResult) object
print(results[0]) # DEMO
results = [res.get() for res in results]
print(f'result: {results}')
Exemple de sortie:
<multiprocessing.queues.SimpleQueue object at 0x7fa124fd67f0>
<multiprocessing.pool.ApplyResult object at 0x7fa12586da20>
result: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Remarque: La spécification du paramètre timeout
- pour .get()
n'arrêtera pas le traitement réel de la tâche au sein du travailleur, il débloque uniquement le parent en attente en levant un multiprocessing.TimeoutError
.