8.4. Gather¶
Az előző fejezet bemutatta, hogyan ütemez az asyncio.create_task() egy korutint, és hogyan nem vár rá. A társművelet – több várhatót egyidejűleg futtatni és mindegyikre megvárni – az asyncio.gather().
8.4.1. Az alapforma¶
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())
Kimenet:
['a', 'b', 'c']
Két dolog figyelhető meg. Először is, az eredménylista a gather argumentumainak sorrendjében van, nem abban a sorrendben, ahogy a korutinok befejeződtek – a "c" tért vissza először, mégis ez a harmadik bejegyzés. Másodszor, a hívás összesen 200 ms ideig tartott, nem 350 ms ideig: a három alvás egyidejűleg futott, és az gather() azonnal visszatér, amint a leglassabb befejeződik.
Mindkét tény ugyanabból a forrásból ered. Az gather() minden olyan argumentumot, amely még nem feladat (a példában a korutinokat), beburkol, ütemezi őket a hurkon, felfüggeszti a hívó korutint, amíg mindegyik be nem fejeződik, majd visszaadja az eredményeiket az eredeti sorrendben.
8.4.2. Mikor érdemes nyúlni hozzá¶
Bárhol, ahol az alkalmazásnak N darab várható művelete van, és mindegyik eredményét meg akarja kapni, mielőtt folytatja. Tipikus példák:
Több hálózati kérés párhuzamos kiküldése és az összes válasz megvárása.
Több érzékelő egyidejű olvasása, mielőtt feldolgozná a kombinált eredményt.
Több rövid életű segédfeladat összevonása egy alkalmazásfázis végén.
Nem ez a megfelelő eszköz a hosszan futó háttérfeladatokhoz, amelyeket az alkalmazás elindít és a program teljes élettartamára futni hagy – azok továbbra is create_task() munka. Az gather() a szétterítés / összegyűjtés mintára való: munkát felosztani, egyidejűleg elvégezni, újraegyesíteni.
8.4.3. Kivételek a csoportban¶
Ha az összegyűjtött várhatók közül bármelyik kivételt vált ki, az alapértelmezett viselkedés az, hogy a kivételt újra kiváltja a gather hívásból. A még be nem fejezett testvéreket a háttérben megszakítja.
Ez általában az, amit egy alkalmazás szeretne – az N párhuzamos feladat egyike meghiúsult, így a kombinált művelet meghiúsult, ezért ne pazaroljunk időt a többire. Néha az alkalmazás az ellenkezőjét akarja: hagyni, hogy minden várható függetlenül befejeződjön (vagy meghiúsuljon), és utólag megvizsgálni az eredményeket. Ehhez add át a return_exceptions=True paramétert:
results = await asyncio.gather(
fetch_or_fail("ok"),
fetch_or_fail("bad"),
return_exceptions=True,
)
# results == ["ok-value", OSError(...)]
A visszaadott lista minden egyes bejegyzése most vagy egy normál visszatérési érték, vagy a kivétel, amelyet a megfelelő várható kiváltott. A hívó az isinstance(r, Exception) ellenőrzéssel különbözteti meg őket.
8.4.4. Megszakítás¶
Magának a gathernek a megszakítása – bármely feladat megszakításával, amely megvárta – megszakít minden benne még futó várhatót. A időtúllépések és megszakítás oldal részletesen tárgyalja, hogyan terjed a megszakítás a hívási láncon.