Asyncio.gather vs asyncio.wait

150

asyncio.gathery asyncio.waitparece tener usos similares: tengo un montón de cosas asíncronas que quiero ejecutar / esperar (no necesariamente esperando que una termine antes de que comience la siguiente). Usan una sintaxis diferente y difieren en algunos detalles, pero me parece muy poco pitónico tener 2 funciones que tienen una superposición tan grande en la funcionalidad. ¿Qué me estoy perdiendo?

Claude
fuente

Respuestas:

178

Aunque es similar en casos generales ("ejecutar y obtener resultados para muchas tareas"), cada función tiene alguna funcionalidad específica para otros casos:

asyncio.gather()

Devuelve una instancia futura, lo que permite la agrupación de tareas de alto nivel:

import asyncio
from pprint import pprint

import random


async def coro(tag):
    print(">", tag)
    await asyncio.sleep(random.uniform(1, 3))
    print("<", tag)
    return tag


loop = asyncio.get_event_loop()

group1 = asyncio.gather(*[coro("group 1.{}".format(i)) for i in range(1, 6)])
group2 = asyncio.gather(*[coro("group 2.{}".format(i)) for i in range(1, 4)])
group3 = asyncio.gather(*[coro("group 3.{}".format(i)) for i in range(1, 10)])

all_groups = asyncio.gather(group1, group2, group3)

results = loop.run_until_complete(all_groups)

loop.close()

pprint(results)

Todas las tareas en un grupo pueden cancelarse llamando group2.cancel()o incluso all_groups.cancel(). Véase también .gather(..., return_exceptions=True),

asyncio.wait()

Admite la espera para detenerse después de que se realiza la primera tarea, o después de un tiempo de espera especificado, lo que permite una precisión de operaciones de menor nivel:

import asyncio
import random


async def coro(tag):
    print(">", tag)
    await asyncio.sleep(random.uniform(0.5, 5))
    print("<", tag)
    return tag


loop = asyncio.get_event_loop()

tasks = [coro(i) for i in range(1, 11)]

print("Get first result:")
finished, unfinished = loop.run_until_complete(
    asyncio.wait(tasks, return_when=asyncio.FIRST_COMPLETED))

for task in finished:
    print(task.result())
print("unfinished:", len(unfinished))

print("Get more results in 2 seconds:")
finished2, unfinished2 = loop.run_until_complete(
    asyncio.wait(unfinished, timeout=2))

for task in finished2:
    print(task.result())
print("unfinished2:", len(unfinished2))

print("Get all other results:")
finished3, unfinished3 = loop.run_until_complete(asyncio.wait(unfinished2))

for task in finished3:
    print(task.result())

loop.close()
Udi
fuente
55
"La forma de asterisco único (* args) se usa para pasar una lista de argumentos de longitud variable sin palabras clave, y la forma de asterisco doble se usa para pasar una lista de argumentos de longitud variable sin
palabras clave
41

asyncio.waites más bajo nivel que asyncio.gather.

Como su nombre lo indica, asyncio.gatherse centra principalmente en recopilar los resultados. espera en un montón de futuros y devuelve sus resultados en un orden determinado.

asyncio.waitsolo espera en el futuro. y en lugar de darle los resultados directamente, le da tareas pendientes y pendientes. tienes que recolectar manualmente los valores.

Además, puede especificar esperar a que finalicen todos los futuros o solo el primero con wait.

ospider
fuente
Usted dice: it waits on a bunch of futures and return their results in a given order. ¿Qué pasa si tengo 10000000000000 tareas y todas ellas devuelven datos grandes? Todo el resultado hará el boom de la memoria?
Kingname
@Kingname ..wat
Matt Joiner
14

También noté que puede proporcionar un grupo de corutinas en wait () simplemente especificando la lista:

result=loop.run_until_complete(asyncio.wait([
        say('first hello', 2),
        say('second hello', 1),
        say('third hello', 4)
    ]))

Mientras que la agrupación en recolectar () se realiza simplemente especificando múltiples corutinas:

result=loop.run_until_complete(asyncio.gather(
        say('first hello', 2),
        say('second hello', 1),
        say('third hello', 4)
    ))
Johny Ebanat
fuente
20
Las listas también se pueden usar con gather(), por ejemplo:asyncio.gather(*task_list)
tehfink
1
Así pueden los generadores
Jab
¿Cómo puedes usar esta reunión sin bloquear el resto del script?
thebeancounter