Asyncio.gather vs asyncio.wait

150

asyncio.gatheri asyncio.waitwydaje się, że mają podobne zastosowania: mam kilka asynchronicznych rzeczy, które chcę wykonać / poczekać (niekoniecznie czekając na zakończenie jednego przed rozpoczęciem następnego). Używają innej składni i różnią się niektórymi szczegółami, ale wydaje mi się bardzo nie-pythoniczne, aby mieć dwie funkcje, które mają tak duże nakładanie się funkcjonalności. czego mi brakuje?

Claude
źródło

Odpowiedzi:

178

Chociaż w ogólnych przypadkach są podobne („uruchom i uzyskaj wyniki dla wielu zadań”), każda funkcja ma określoną funkcjonalność w innych przypadkach:

asyncio.gather()

Zwraca instancję Future, umożliwiając grupowanie zadań na wysokim poziomie:

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)

Wszystkie zadania w grupie można anulować dzwoniąc group2.cancel()lub nawet all_groups.cancel(). Zobacz także .gather(..., return_exceptions=True),

asyncio.wait()

Obsługuje oczekiwanie na zatrzymanie po wykonaniu pierwszego zadania lub po określonym czasie, umożliwiając niższy poziom precyzji operacji:

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
źródło
5
„Pojedyncza gwiazdka (* args) jest używana do przekazywania listy argumentów o zmiennej długości bez słów kluczowych, a podwójna gwiazdka służy do przekazywania listy argumentów o zmiennej długości ze
słowami
41

asyncio.waitjest niższy niż asyncio.gather.

Jak sama nazwa wskazuje, asyncio.gatherskupia się głównie na zbieraniu wyników. czeka na kilka kontraktów futures i zwraca ich wyniki w zadanej kolejności.

asyncio.waitpo prostu czeka na przyszłość. i zamiast bezpośrednio podawać wyniki, podaje zadania wykonane i oczekujące. musisz ręcznie zbierać wartości.

Co więcej, możesz określić, czy chcesz czekać, aż wszystkie futures się skończą, czy tylko pierwsza wait.

ospider
źródło
Mówisz: it waits on a bunch of futures and return their results in a given order. co jeśli mam 10000000000000 zadań i wszystkie zwracają duże dane? cały wynik spowoduje boom pamięci?
Kingname
@Kingname ..wat
Matt Joiner
14

Zauważyłem również, że możesz podać grupę programów w oczekiwaniu (), po prostu określając listę:

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

Podczas gdy grupowanie w funkcji Gather () odbywa się po prostu przez określenie wielu procedur:

result=loop.run_until_complete(asyncio.gather(
        say('first hello', 2),
        say('second hello', 1),
        say('third hello', 4)
    ))
Johny Ebanat
źródło
20
Listy mogą być również używane z gather()np .:asyncio.gather(*task_list)
tehfink
1
Tak samo jak generatory
Jab
Jak możesz używać tego zbieracza bez blokowania reszty skryptu?
licznik fasoli