8.4. Gather

Edellinen luku näytti, miten asyncio.create_task() ajastaa korutiinin eikä odota sitä. Sen rinnakkaisoperaatio – suorita useita odotettavia rinnakkain ja odota niitä kaikkia – on asyncio.gather().

8.4.1. Perusmuoto

import asyncio

async def fetch(name, delay_ms):
    await asyncio.sleep_ms(delay_ms)
    return name

async def main():
    results = await asyncio.gather(
        fetch("a", 100),
        fetch("b", 200),
        fetch("c", 50),
    )
    print(results)

asyncio.run(main())

Tuloste:

['a', 'b', 'c']

Kaksi huomioitavaa asiaa. Ensinnäkin tuloslista on gatherin argumenttien järjestyksessä, ei siinä järjestyksessä, jossa korutiinit päättyivät – "c" palautui ensimmäisenä, mutta on silti kolmas merkintä. Toiseksi kutsu kesti yhteensä 200 ms, ei 350 ms: kolme nukkumista suoritettiin rinnakkain, ja gather() palautuu heti kun hitain niistä valmistuu.

Molemmat tosiasiat juontuvat samasta lähteestä. gather() käärii jokaisen argumentin, joka ei jo ole tehtävä (esimerkin korutiinit), ajastaa ne silmukkaan, keskeyttää kutsuvan korutiinin kunnes kaikki niistä päättyvät, ja palauttaa sitten niiden tulokset alkuperäisessä järjestyksessä.

8.4.2. Milloin tarttua siihen

Aina kun sovelluksella on N odotettavaa operaatiota ja se haluaa kaikkien niiden tulokset ennen jatkamista. Tyypillisiä esimerkkejä:

  • Useiden verkkopyyntöjen lähettäminen rinnakkain ja kaikkien vastausten odottaminen.

  • Lukeminen useilta sensoreilta rinnakkain ennen yhdistetyn tuloksen käsittelyä.

  • Useiden lyhytikäisten apu-tehtävien yhdistäminen sovellusvaiheen lopussa.

Se ei ole oikea työkalu pitkäkestoisille taustatehtäville, jotka sovellus käynnistää ja jättää käymään koko ohjelman elinkaaren ajaksi – ne ovat yhä create_task()-työtä. gather() on tarkoitettu fan-out / fan-in -malliin: jaa työ, tee se rinnakkain, yhdistä uudelleen.

8.4.3. Poikkeukset ryhmässä

Jos jokin kerätyistä odotettavista nostaa poikkeuksen, oletuskäyttäytyminen on nostaa poikkeus uudelleen ulos gather-kutsusta. Vielä valmistumattomat sisarukset perutaan taustalla.

Tämä on yleensä se, mitä sovellus haluaa – yksi N:stä rinnakkaisesta työstä epäonnistui, joten yhdistetty operaatio on epäonnistunut, joten lopeta ajan käyttäminen muihin. Joskus sovellus haluaa päinvastaisen: anna jokaisen odotettavan päättyä (tai epäonnistua) itsenäisesti, ja tarkasta tulokset jälkikäteen. Anna sitä varten return_exceptions=True

results = await asyncio.gather(
    fetch_or_fail("ok"),
    fetch_or_fail("bad"),
    return_exceptions=True,
)
# results == ["ok-value", OSError(...)]

Jokainen merkintä palautetussa listassa on nyt joko normaali paluuarvo tai vastaavan odotettavan nostama poikkeus. Kutsuja tarkistaa isinstance(r, Exception) erottaakseen ne toisistaan.

8.4.4. Peruminen

Itse gatherin peruminen – perumalla mikä tahansa tehtävä, joka sitä odotti – perii jokaisen sen sisällä yhä käynnissä olevan odotettavan. timeouts and cancellation -sivulla käsitellään yksityiskohtaisesti, miten peruminen etenee kutsuketjun läpi.