Taken binnen een async context een gezamenlijke levensduur geven.
Resultaten pas na een succesvolle groepsafronding uitlezen.
Een foutgroep en de gevolgen voor overige taken onderscheiden van het gather-beleid.
Een rapport heeft twee onderdelen nodig. Zodra één onderdeel onbruikbaar blijkt, wil je het andere werk niet ongemerkt laten doorlopen. Bovendien wil je pas aan de volgende stap beginnen wanneer alle gestarte onderdelen werkelijk zijn afgerond of opgeruimd.
Een groep is ook een afspraak over levensduur
Een TaskGroup beheert verwante taken binnen een async contextmanager. Je kent with al voor bijvoorbeeld bestanden. async with laat het betreden en verlaten van een context ook async werk afwachten.
Binnen async with asyncio.TaskGroup() as group voeg je taken toe met group.create_task(coroutine). De groep bewaart hun referenties en wacht bij het verlaten van het blok op alle taken. De regel direct na het blok wordt daardoor niet uitgevoerd terwijl een normale groepstaak nog bezig is.
Het voorbeeld maakt één taak. Pas na de groepscontext wordt haar resultaat uitgelezen. Je hoeft geen wachttijd te raden: het verlaten van de context vormt de afspraak dat dit werk klaar is.
Resultaten zelf bijhouden
Een TaskGroup levert niet automatisch een lijst resultaten zoals gather. Bewaar daarom de Task-objecten wanneer je die waarden later nodig hebt. Na een succesvolle context kun je met task.result() de waarde van iedere afgeronde taak opvragen.
De lijst waarin jij de taakreferenties bewaart bepaalt de resultaatvolgorde. Een snelle taak hoeft niet vooraan te komen. Maak voor iedere async bron eenmaal een taak en lees na afloop de referenties in de afgesproken invoervolgorde uit.
Ook een groep zonder taken heeft een geldig einde. In de oefenfunctie hoort daar een lege resultatenlijst bij. De groepscontext zorgt voor de levensduur; jouw code blijft verantwoordelijk voor de vorm en volgorde van de teruggegeven waarden.
Wanneer één onderdeel faalt
Als een groepstaak een gewone fout krijgt, vraagt de TaskGroup om annulering van de overige groepstaken. Daarna wacht zij ook op het einde van die taken. Een finally-blok in zo'n taak krijgt daardoor de gelegenheid om op te ruimen.
De groep neemt die opruiming niet van de taak over. De taak moet haar eigen middelen correct vrijgeven en annulering niet blijven negeren. In de volgende les oefen je dat gedrag zelf; hier leveren de meegegeven bronnen hun eigen opruimcode mee.
Wanneer het groepswerk is afgerond, worden gewone taakfouten samengebracht in een ExceptionGroup. Zo kunnen meerdere problemen als één geheel naar de aanroeper gaan. Een groep kan ook weer andere groepen bevatten; het is daarom niet altijd één platte lijst fouten.
Een gewone fout komt als groep naar buiten
import asyncio
async def price():
await asyncio.sleep(0)
raise ValueError("bedrag ontbreekt")
async def main():
try:
async with asyncio.TaskGroup() as group:
group.create_task(price())
except ExceptionGroup as problem:
print(type(problem).__name__)
for error in problem.exceptions:
print(type(error).__name__ + ": " + str(error))
asyncio.run(main())
Verwachte uitvoer
ExceptionGroup
ValueError: bedrag ontbreekt
Dit voorbeeld heeft één gewone ValueError en laat zien dat de aanroeper een foutgroep ontvangt. Het bekijkt de onderdelen van precies deze eenvoudige groep. Daarmee wordt nog niet aangenomen dat iedere mogelijke foutgroep dezelfde platte vorm heeft.
Veelgemaakte fout: te vroeg een resultaat vragen
task.result() wacht niet. Als de taak nog niet klaar is, kan die aanroep een InvalidStateError opleveren. Zet het verzamelen van de waarden daarom na de succesvolle async with-context, niet direct achter iedere create_task-aanroep.
Een mislukte groep bereikt die resultaatregel normaal niet: haar uitzondering gaat eerst door naar de aanroeper. Maak er niet stil een lege lijst van. Dan lijkt een mislukt rapport op een geslaagd rapport zonder gegevens.
Als de aanroeper stopt
De aanroeper kan de taak die de groep beheert ook zelf annuleren. De groepscontext handelt dan haar onderliggende taken af voordat die annulering naar buiten doorgaat. Gewone foutgroepen en zo'n externe annulering hebben een andere betekenis; slik geen van beide zonder besluit in.
Alle vier de checkpoints bekijken dezelfde oefenfunctie. Je bouwt één bruikbare groepsbewerking met verschillende gedragsafspraken: waarden verzamelen, bronnen samen bereiken, fouten doorgeven en bij stoppen geen eigen taken achterlaten.
AAN DE SLAG 0/4
1
Waarden na de groep
Bouw async run_group(jobs) met een TaskGroup. De lijst bevat async functies zonder argumenten. Maak voor elke bron eenmaal een groepstaak en geef na succesvolle afronding de waarden als lijst in invoervolgorde terug. Een lege groep geeft [].
2
Samen beginnen en samen eindigen
Laat run_group ook werken wanneer de bronnen op elkaar wachten. Alle bronnen moeten kunnen beginnen; bij de terugkeer zijn de eigen taken afgerond. Bewaar de invoervolgorde terwijl de bronnen in een andere volgorde klaar kunnen zijn.
3
Een groepsfout laten doorgaan
Behoud het gewone TaskGroup-foutbeleid van run_group. Een fout van een bron moet in de foutgroep blijven staan als het oorspronkelijke exceptionobject. De andere bron wordt geannuleerd en haar meegeleverde finally is afgerond voordat de fout naar buiten komt.
4
Stoppen door de aanroeper
Laat een externe annulering van run_group doorgaan. Ook dan moeten de twee reeds begonnen bronnen hun eigen finally afronden voordat de beheertaak klaar is. Er blijft geen eigen bronwerk achter.
Samenvatting
Een TaskGroup wacht bij het verlaten van haar async context op het beheerde werk.
Bewaar referenties voor resultaatvolgorde en vraag resultaten na succesvolle afronding op.
Bij een gewone taakfout worden overige groepstaken geannuleerd en afgehandeld; fouten blijven zichtbaar als groep.
Hierna bekijk je wat annulering binnen een coroutine betekent en hoe haar eigen opruiming werkt.