multiprocessing.Pool: Quand utiliser apply, apply_async ou map?

Réponses:

424

À l'époque de Python, pour appeler une fonction avec des arguments arbitraires, vous utilisiez apply:

apply(f,args,kwargs)

applyexiste toujours en Python2.7 mais pas en Python3, et n'est généralement plus utilisé. Aujourd'hui,

f(*args,**kwargs)

est préféré. Les multiprocessing.Poolmodules essaient de fournir une interface similaire.

Pool.applyest comme Python apply, sauf que l'appel de fonction est effectué dans un processus séparé. Pool.applybloque jusqu'à ce que la fonction soit terminée.

Pool.apply_asyncest également similaire à Python apply, sauf que l'appel retourne immédiatement au lieu d'attendre le résultat. Un AsyncResultobjet est retourné. Vous appelez sa get()méthode pour récupérer le résultat de l'appel de fonction. La get()méthode se bloque jusqu'à ce que la fonction soit terminée. Ainsi, pool.apply(func, args, kwargs)est équivalent à pool.apply_async(func, args, kwargs).get().

Contrairement à Pool.apply, la Pool.apply_asyncméthode a également un rappel qui, s'il est fourni, est appelé lorsque la fonction est terminée. Cela peut être utilisé au lieu d'appeler get().

Par exemple:

import multiprocessing as mp
import time

def foo_pool(x):
    time.sleep(2)
    return x*x

result_list = []
def log_result(result):
    # This is called whenever foo_pool(i) returns a result.
    # result_list is modified only by the main process, not the pool workers.
    result_list.append(result)

def apply_async_with_callback():
    pool = mp.Pool()
    for i in range(10):
        pool.apply_async(foo_pool, args = (i, ), callback = log_result)
    pool.close()
    pool.join()
    print(result_list)

if __name__ == '__main__':
    apply_async_with_callback()

peut donner un résultat tel que

[1, 0, 4, 9, 25, 16, 49, 36, 81, 64]

Remarquez, contrairement à pool.map, l'ordre des résultats peut ne pas correspondre à l'ordre dans lequel les pool.apply_asyncappels ont été effectués.


Donc, si vous devez exécuter une fonction dans un processus distinct, mais que vous souhaitez que le processus actuel se bloque jusqu'à ce que cette fonction revienne, utilisez Pool.apply. Comme Pool.apply, Pool.mapbloque jusqu'à ce que le résultat complet soit retourné.

Si vous souhaitez que le pool de processus de travail effectue de nombreux appels de fonction de manière asynchrone, utilisez Pool.apply_async. L' ordre des résultats n'est pas garanti d'être le même que l'ordre des appels à Pool.apply_async.

Notez également que vous pouvez appeler un certain nombre de fonctions différentes avec Pool.apply_async(tous les appels n'ont pas besoin d'utiliser la même fonction).

En revanche, Pool.mapapplique la même fonction à de nombreux arguments. Cependant, contrairement Pool.apply_asyncaux résultats, les résultats sont renvoyés dans un ordre correspondant à l'ordre des arguments.

unutbu
la source
11
Devrait-il y avoir if __name__=="__main__"avant apply_async_with_callback()sur Windows?
jfs
3
Merci beaucoup. que diriez-vous map_async?
Phyo Arkar Lwin
38
Regardez dans multiprocessing / pool.py et vous verrez que Pool.map(func,iterable)c'est équivalent à Pool.map_async(func,iterable).get(). La relation entre Pool.mapet Pool.map_asyncest donc similaire à celle de Pool.applyet Pool.apply_async. Les asynccommandes reviennent immédiatement, tandis que le asyncbloc non- commandes. Les asynccommandes ont également un rappel.
unutbu
7
Décider entre utiliser Pool.mapet Pool.applyest similaire à décider quand utiliser mapou applyen Python. Vous utilisez simplement l'outil qui convient au travail. Le choix entre l'utilisation de la asyncet de la non- asyncversion dépend de si vous souhaitez que l'appel bloque le processus en cours et / ou si vous souhaitez utiliser le rappel.
unutbu
6
@falsePockets: Oui. Chaque appel à apply_asyncrenvoie un ApplyResultobjet. L' appel que ApplyResultde » getla méthode retourne la valeur de retour de la fonction associée (ou augmentation mp.TimeoutErrorsi les temps-out appel.) Donc , si vous mettez le ApplyResults dans une liste ordonnée, puis en appelant leurs getméthodes renverra les résultats dans le même ordre. Vous pouvez simplement utiliser pool.mapdans cette situation cependant.
unutbu
75

Concernant applyvs map:

pool.apply(f, args): fn'est exécuté que dans UN des travailleurs de la piscine. Donc, l'un des processus du pool s'exécutera f(args).

pool.map(f, iterable): Cette méthode coupe l'itérable en plusieurs morceaux qu'elle soumet au pool de processus en tant que tâches distinctes. Vous profitez donc de tous les processus du pool.

kakhkAtion
la source
4
et si l'itérable est un générateur
RustyShackleford
Hmm ... Bonne question. Pour être honnête, je n'ai jamais utilisé de pools avec des générateurs, mais ce fil pourrait être utile: stackoverflow.com/questions/5318936/…
kakhkAtion
@kakhkAtion En ce qui concerne l'application, si un seul des travailleurs exécute la fonction, que font les autres travailleurs? Dois-je appeler plusieurs fois pour demander au reste des employés d'effectuer une tâche?
Moondra
3
Vrai. Jetez également un œil à pool.apply_async si vous souhaitez déjeuner les travailleurs de manière asynchrone. "pool_apply bloque jusqu'à ce que le résultat soit prêt, donc apply_async () est mieux adapté pour effectuer un travail en parallèle"
kakhkAtion
1
Que se passe-t-il lorsque j'ai 4 processus mais que j'ai appelé apply_async()8 fois? Le traitera-t-il automatiquement avec une file d'attente?
Saravanabalagi Ramachandran
31

Voici un aperçu dans un format de table afin de montrer les différences entre Pool.apply, Pool.apply_async, Pool.mapet Pool.map_async. Lorsque vous en choisissez un, vous devez prendre en compte plusieurs arguments, la concurrence, le blocage et la commande:

                  | Multi-args   Concurrence    Blocking     Ordered-results
---------------------------------------------------------------------
Pool.map          | no           yes            yes          yes
Pool.map_async    | no           yes            no           yes
Pool.apply        | yes          no             yes          no
Pool.apply_async  | yes          yes            no           no
Pool.starmap      | yes          yes            yes          yes
Pool.starmap_async| yes          yes            no           no

Remarques:

  • Pool.imapet Pool.imap_async- version paresseuse de map et map_async.

  • Pool.starmap , très similaire à la méthode map en plus de l'acceptation de plusieurs arguments.

  • AsyncLes méthodes soumettent tous les processus à la fois et récupèrent les résultats une fois qu'ils sont terminés. Utilisez la méthode get pour obtenir les résultats.

  • Pool.map(ou Pool.apply) les méthodes sont très similaires à la carte intégrée Python (ou s'appliquent). Ils bloquent le processus principal jusqu'à ce que tous les processus soient terminés et renvoient le résultat.

Exemples:

carte

Est appelé pour une liste d'emplois en une seule fois

results = pool.map(func, [1, 2, 3])

appliquer

Ne peut être appelé que pour un seul emploi

for x, y in [[1, 1], [2, 2]]:
    results.append(pool.apply(func, (x, y)))

def collect_result(result):
    results.append(result)

map_async

Est appelé pour une liste d'emplois en une seule fois

pool.map_async(func, jobs, callback=collect_result)

apply_async

Ne peut être appelé que pour un seul travail et exécute un travail en arrière-plan en parallèle

for x, y in [[1, 1], [2, 2]]:
    pool.apply_async(worker, (x, y), callback=collect_result)

starmap

Est une variante pool.mapqui prend en charge plusieurs arguments

pool.starmap(func, [(1, 1), (2, 1), (3, 1)])

starmap_async

Une combinaison de starmap () et map_async () qui itère sur l'itérable des itérables et appelle func avec les itérables décompressés. Renvoie un objet résultat.

pool.starmap_async(calculate_worker, [(1, 1), (2, 1), (3, 1)], callback=collect_result)

Référence:

Retrouvez la documentation complète ici: https://docs.python.org/3/library/multiprocessing.html

René B.
la source
2
Pool.starmap () bloque
Alan Evangelista