8.4. Gather¶
Det föregående kapitlet visade hur asyncio.create_task() schemalägger en korutin och inte väntar på den. Motsvarande operation – kör flera väntbara samtidigt och vänta på alla – är asyncio.gather().
8.4.1. Grundformen¶
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())
Utdata:
['a', 'b', 'c']
Två saker att lägga märke till. Först, resultatlistan följer ordningen för argumenten till gather, inte den ordning i vilken korutinerna avslutades – "c" returnerade först men är fortfarande den tredje posten. För det andra tog anropet 200 ms totalt, inte 350 ms: de tre sovperioderna kördes samtidigt, och gather() returnerar så snart den långsammaste slutförs.
Båda fakta kommer från samma källa. gather() omsluter varje argument som inte redan är en uppgift (korutinerna i exemplet), schemalägger dem på loopen, pausar den anropande korutinen tills alla har slutförts och returnerar sedan deras resultat i den ursprungliga ordningen.
8.4.2. När man ska ta till den¶
Överallt där applikationen har N väntbara operationer och vill ha resultaten av alla innan den fortsätter. Typiska exempel:
Skicka flera nätverksförfrågningar parallellt och vänta på alla svar.
Läsa från flera sensorer samtidigt innan det kombinerade resultatet bearbetas.
Sammanfoga flera kortlivade hjälpuppgifter i slutet av en applikationsfas.
Det är inte rätt verktyg för långkörande bakgrundsuppgifter som applikationen startar och låter köra under programmets livstid – de är fortfarande ett jobb för create_task(). gather() är till för mönstret fan-out / fan-in: dela upp arbete, gör det samtidigt, sammanfoga.
8.4.3. Undantag i gruppen¶
Om någon av de samlade väntbara kastar ett undantag är standardbeteendet att kasta undantaget på nytt ut ur gather-anropet. Syskonen som ännu inte har slutförts avbryts i bakgrunden.
Det är vanligtvis vad en applikation vill – ett av N parallella jobb misslyckades, så den kombinerade operationen har misslyckats, alltså sluta lägga tid på resten. Ibland vill applikationen ha det motsatta: låt varje väntbar slutföras (eller misslyckas) oberoende, och inspektera resultaten efteråt. Skicka return_exceptions=True för det:
results = await asyncio.gather(
fetch_or_fail("ok"),
fetch_or_fail("bad"),
return_exceptions=True,
)
# results == ["ok-value", OSError(...)]
Varje post i den returnerade listan är nu antingen ett normalt returvärde eller undantaget som motsvarande väntbar kastade. Anroparen kontrollerar isinstance(r, Exception) för att skilja dem åt.
8.4.4. Avbrytning¶
Att avbryta själva gather – genom att avbryta vilken uppgift som än väntade på den – avbryter varje väntbar som fortfarande körs inuti den. Sidan timeouts och avbrytning täcker i detalj hur avbrytning propagerar genom anropskedjan.