← Python/Concurrency
Les 2 van 8
MODULE 12 · 02◷ 30 min

Onafhankelijk werk als taken starten

Na deze les kun je
  • Een coroutine met create_task als Task laten plannen.
  • Taakreferenties bewaren en de echte resultaten afwachten.
  • Onafhankelijke bronnen laten overlappen en een afgerond taakresultaat opnieuw gebruiken.

Je zet de oven aan en ruimt ondertussen de tafel af. Wanneer je eerst naast de oven blijft wachten en pas daarna de tafel aanpakt, benut je die wachttijd niet. Bij twee onafhankelijke async bewerkingen kun je op een vergelijkbare manier eerst het werk laten plannen en daarna de resultaten afwachten.

Van coroutine naar taak

In de vorige les maakte een aanroep van een coroutinefunctie alleen een coroutineobject. Met asyncio.create_task bied je dat object aan de lopende eventloop aan. Je krijgt een Task terug: een object dat de geplande uitvoering en later ook haar resultaat of fout bijhoudt.

Maak taken binnen een lopend async programma. Een losse asyncio.create_task(job()) bovenaan een gewoon script heeft nog geen draaiende loop om op te plannen. Gebruik een async hoofdfunctie die je onderaan met asyncio.run(main()) start.

Bewaar de teruggegeven taakreferentie. Daarmee kun je wachten op precies die uitvoering, controleren of zij klaar is en later zo nodig om annulering vragen. Het is overzichtelijker wanneer de code die werk start ook verantwoordelijk is voor het afwachten ervan.

Eerst beide starten

Schrijf eerst first_task = asyncio.create_task(first()) en maak vervolgens de tweede taak. Pas daarna gebruik je await first_task en await second_task. Als de eerste op iets moet wachten, kan de eventloop ondertussen de tweede laten lopen.

Eerst twee taken maken
import asyncio

async def job(name):
    print("begin", name)
    await asyncio.sleep(0)
    print("klaar", name)
    return name.upper()

async def main():
    oven = asyncio.create_task(job("oven"))
    table = asyncio.create_task(job("tafel"))
    print(await oven)
    print(await table)

asyncio.run(main())
Verwachte uitvoer
begin oven
begin tafel
klaar oven
klaar tafel
OVEN
TAFEL

Het voorbeeld drukt zowel het begin als het einde van twee vaste oefenklussen af. De oefenpauzes laten ander gepland werk toe. Bij echte bronnen kan de volgorde waarin klussen klaar zijn verschillen; het programma vraagt de resultaten hier bewust in een vaste volgorde op.

Een Task laten plannen betekent niet dat haar resultaat onmiddellijk beschikbaar is. task.done() vertelt alleen of de taak al is afgerond. Ook een taak die met een fout of annulering eindigt is klaar; done() is dus geen bevestiging dat het werk succesvol was.

Wachten levert de waarde op

Met answer = await task krijg je de geretourneerde waarde van de geplande coroutine. Een fout gaat op dit punt door naar de aanroeper. Geef dus de waarde terug wanneer de opdracht om een resultaat vraagt, en niet het Task-object zelf.

Een afgeronde Task bewaart haar resultaat. Anders dan een los coroutineobject kun je dezelfde Task later opnieuw afwachten. Dat start de oorspronkelijke coroutine niet nog een keer en leest de bron dus niet opnieuw.

Een afgeronde taak onthoudt haar resultaat
import asyncio

reads = []

async def read_count():
    reads.append("gelezen")
    await asyncio.sleep(0)
    return 5

async def main():
    task = asyncio.create_task(read_count())
    print(await task)
    print(await task)
    print(len(reads))

asyncio.run(main())
Verwachte uitvoer
5
5
1

De teller in dit voorbeeld verandert maar eenmaal, terwijl het resultaat tweemaal verschijnt. Een verse aanroep van de coroutinefunctie zou juist nieuw werk beschrijven. Dat onderscheid is belangrijk wanneer lezen geld kost of een bewerking iets wijzigt.

Veelgemaakte fout: achter elkaar starten

Bij left = await first() en daarna right = await second() is er bewust nog geen overlap. Ook await asyncio.create_task(first()) gevolgd door het starten van de tweede taak wacht eerst de volledige eerste taak af. Alleen het woord Task toevoegen verandert die volgorde niet.

In de opdrachten kunnen de twee meegegeven leesfuncties onafhankelijk werken. Eén oefenbron wacht totdat ook de andere is begonnen. Wie maar één bron start en daarop blijft wachten, krijgt daarom geen gezamenlijk resultaat.

Verantwoordelijkheid blijft bij de starter

Laat geen taken achter waarvan niemand het resultaat nog opvraagt. In deze les rond je alle gestarte taken af voordat je terugkeert. Foutgroepen en gezamenlijk opruimen komen hierna aan bod; de huidige opdrachten gebruiken bronnen die normaal een waarde teruggeven.

De eerste oefenfunctie is bewust een gewone def: zij maakt twee taken en geeft meteen hun referenties terug. De overige functies zijn async, omdat zij zelf op taakresultaten wachten. Je weet zo aan de functievorm of de aanroeper nog een coroutine moet afwachten.

AAN DE SLAG 0/4
1

Twee referenties teruggeven

Schrijf gewone def start_pair(first, second). Roep beide async functies zonder argumenten eenmaal aan, plan elk als een eigen asyncio.Task en geef (first_task, second_task) als tuple terug. De functie wordt binnen een lopende eventloop aangeroepen. Laat bij terugkeer geen andere zelfgestarte taak onafgehandeld achter.

2

Onafhankelijke lezers

Maak async pair_values(first, second). Start beide async leesfuncties eenmaal voordat je op een resultaat wacht. Geef hun waarden als lijst [eerste, tweede] terug, ook als een bron eerst op de andere moet wachten. Laat bij terugkeer geen andere zelfgestarte taak onafgehandeld achter.

3

Klaar is een toestand

Maak in async task_state(work) één Task voor work(). Geef een tuple (klaar_vóór_wachten, resultaat, klaar_na_wachten) terug. De oefenbron wacht op een gebeurtenis, dus de taak is aanvankelijk nog niet klaar. Laat bij terugkeer geen andere zelfgestarte taak onafgehandeld achter.

4

Een resultaat hergebruiken

Voer in async reuse_task_result(work) de async bron precies eenmaal in een eigen Task uit. Geef haar resultaat tweemaal als tuple terug zonder nieuw bronwerk te starten. Je mag dezelfde afgeronde Task opnieuw afwachten of de al ontvangen waarde hergebruiken. Laat bij terugkeer geen andere zelfgestarte taak onafgehandeld achter.

Samenvatting

  • create_task plant een coroutine op de lopende loop en geeft een Task terug.
  • Start onafhankelijk werk voordat je op het eerste resultaat wacht.
  • Bewaar taakreferenties, wacht de resultaten af en hergebruik een afgerond resultaat zonder de bron opnieuw te starten.

Hierna verzamel je meerdere taakresultaten met asyncio.gather en onderzoek je wat er bij een fout gebeurt.

task_tools.py
UitvoerPython
›
›_

Hier komt je resultaat.

Voer je code uit en ontdek
wat je hebt gemaakt.

0/4 stappen
EVEN MEEKIJKEN

Bekijk hoe het kan.

Je mag de oplossing bekijken. Deze poging telt dan niet mee als afgeronde les. Met Reset kun je daarna opnieuw zelf oefenen.