multiprocesamiento.Piscina: ¿Cuándo usar apply, apply_async o map?

Respuestas:

424

En los viejos tiempos de Python, para llamar a una función con argumentos arbitrarios, usaría apply:

apply(f,args,kwargs)

applytodavía existe en Python2.7 aunque no en Python3, y generalmente ya no se usa. Hoy en día,

f(*args,**kwargs)

se prefiere. Los multiprocessing.Poolmódulos intentan proporcionar una interfaz similar.

Pool.applyes como Python apply, excepto que la llamada a la función se realiza en un proceso separado. Pool.applybloques hasta que se complete la función.

Pool.apply_asynctambién es como Python incorporado apply, excepto que la llamada regresa inmediatamente en lugar de esperar el resultado. Se AsyncResultdevuelve un objeto. Llama a su get()método para recuperar el resultado de la llamada a la función. El get()método se bloquea hasta que se completa la función. Por lo tanto, pool.apply(func, args, kwargs)es equivalente a pool.apply_async(func, args, kwargs).get().

En contraste con Pool.apply, el Pool.apply_asyncmétodo también tiene una devolución de llamada que, si se proporciona, se llama cuando se completa la función. Esto se puede usar en lugar de llamar get().

Por ejemplo:

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

puede producir un resultado como

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

Tenga en cuenta que, a diferencia pool.map, el orden de los resultados puede no corresponder al orden en que se realizaron las pool.apply_asyncllamadas.


Por lo tanto, si necesita ejecutar una función en un proceso separado, pero desea que el proceso actual se bloquee hasta que esa función regrese, use Pool.apply. Como Pool.apply, Pool.mapbloques hasta que se devuelva el resultado completo.

Si desea que el grupo de procesos de trabajo realice muchas llamadas de función de forma asincrónica, use Pool.apply_async. El orden de los resultados no se garantiza que sea el mismo que el orden de las llamadas a Pool.apply_async.

Observe también que puede llamar a varias funciones diferentes con Pool.apply_async(no todas las llamadas necesitan usar la misma función).

En contraste, Pool.mapaplica la misma función a muchos argumentos. Sin embargo, a diferencia Pool.apply_async, los resultados se devuelven en un orden correspondiente al orden de los argumentos.

unutbu
fuente
11
¿Debería haber if __name__=="__main__"antes apply_async_with_callback()en Windows?
jfs
3
Muchas gracias. ¿qué tal map_async?
Phyo Arkar Lwin
38
Mire dentro de multiprocessing / pool.py y verá que Pool.map(func,iterable)es equivalente a Pool.map_async(func,iterable).get(). Entonces, la relación entre Pool.mapy Pool.map_asynces similar a la de Pool.applyy Pool.apply_async. Los asynccomandos regresan inmediatamente, mientras que los que no son asynccomandos bloquean. Los asynccomandos también tienen una devolución de llamada.
unutbu
77
Decidir entre usar Pool.mapy Pool.applyes similar a decidir cuándo usar mapo applyen Python. Simplemente usa la herramienta que se adapta al trabajo. Decidir entre usar asynco no la asyncversión depende de si desea que la llamada bloquee el proceso actual y / o si desea usar la devolución de llamada.
unutbu
66
@falsePockets: Sí. Cada llamada a apply_asyncdevuelve un ApplyResultobjeto. Llamando a que ApplyResult's getmétodo devolverá el valor devuelto por la función asociada (o aumento de sueldo mp.TimeoutErrorsi el tiempo de espera del call.) Así que si te ponen los ApplyResults en una lista ordenada, a continuación, llamar a sus getmétodos devolverá los resultados en el mismo orden. pool.mapSin embargo, podría usar en esta situación.
unutbu
75

En cuanto a applyvs map:

pool.apply(f, args): fsolo se ejecuta en UNO de los trabajadores de la agrupación. Por lo tanto, se ejecutará UNO de los procesos del grupo f(args).

pool.map(f, iterable): Este método corta el iterable en varios fragmentos que envía al grupo de procesos como tareas separadas. Entonces aprovecha todos los procesos en el grupo.

kakhkAtion
fuente
44
¿Qué pasa si el iterable es un generador
RustyShackleford
Hmm ... Buena pregunta. Para ser sincero, nunca he usado grupos con generadores, pero este hilo podría ser útil: stackoverflow.com/questions/5318936/…
kakhkAtion
@kakhkAtion Con respecto a la solicitud, si solo uno de los trabajadores ejecuta la función, ¿qué hace el resto de los trabajadores? ¿Tengo que llamar a aplicar varias veces para que el resto de los trabajadores realice una tarea?
Moondra
3
Cierto. También eche un vistazo a pool.apply_async si desea almorzar a los trabajadores de forma asincrónica. "pool_apply bloquea hasta que el resultado esté listo, por lo que apply_async () es más adecuado para realizar trabajos en paralelo"
kakhkAtion
1
¿Qué sucede cuando tengo 4 procesos pero he llamado apply_async()8 veces? ¿Lo manejará automáticamente con una cola?
Saravanabalagi Ramachandran
31

He aquí un resumen en forma de tabla con el fin de mostrar las diferencias entre Pool.apply, Pool.apply_async, Pool.mapy Pool.map_async. Al elegir uno, debe tener en cuenta los argumentos múltiples, la concurrencia, el bloqueo y los pedidos:

                  | 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

Notas:

  • Pool.imapy Pool.imap_async- versión más perezosa de map y map_async.

  • Pool.starmap método, muy similar al método de mapa además de la aceptación de múltiples argumentos.

  • AsyncLos métodos envían todos los procesos a la vez y recuperan los resultados una vez que han terminado. Use el método get para obtener los resultados.

  • Pool.map(o Pool.apply) los métodos son muy similares al mapa incorporado de Python (o se aplican). Bloquean el proceso principal hasta que todos los procesos se completen y devuelvan el resultado.

Ejemplos:

mapa

Se solicita una lista de trabajos al mismo tiempo

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

aplicar

Solo se puede llamar para un trabajo

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

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

map_async

Se solicita una lista de trabajos al mismo tiempo

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

apply_async

Solo se puede llamar para un trabajo y ejecuta un trabajo en segundo plano en paralelo

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

mapa estelar

Es una variante de la pool.mapcual soporta múltiples argumentos

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

starmap_async

Una combinación de starmap () y map_async () que itera sobre iterable de iterables y llama a func con los iterables desempaquetados. Devuelve un objeto de resultado.

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

Referencia:

Encuentre la documentación completa aquí: https://docs.python.org/3/library/multiprocessing.html

Rene B.
fuente
2
Pool.starmap () está bloqueando
Alan Evangelista