Coroutines och Tasks

Detta avsnitt beskriver asyncio-API:er på hög nivå för att arbeta med coroutines och Tasks.

Coroutines

Källkod: Lib/asyncio/coroutines.py


Coroutines deklarerade med async/await-syntaxen är det föredragna sättet att skriva asyncio-applikationer. Följande kodavsnitt skriver till exempel ut ”hello”, väntar 1 sekund och skriver sedan ut ”world”:

>>> import asyncio

>>> async def main():
...     print('hello')
...     await asyncio.sleep(1)
...     print('world')

>>> asyncio.run(main())
hello
world

Observera att det inte räcker med att anropa en coroutine för att den ska exekveras:

>>> main()
<coroutine object main at 0x1053bb7c8>

För att faktiskt köra en coroutine tillhandahåller asyncio följande mekanismer:

  • Funktionen asyncio.run() för att köra funktionen ”main()” på den högsta nivån (se exemplet ovan)

  • Väntar på en coroutine. Följande kodavsnitt kommer att skriva ut ”hello” efter att ha väntat i 1 sekund, och sedan skriva ut ”world” efter att ha väntat i ytterligare 2 sekunder:

    import asyncio
    import time
    
    async def say_after(delay, what):
        await asyncio.sleep(delay)
        print(what)
    
    async def main():
        print(f"started at {time.strftime('%X')}")
    
        await say_after(1, 'hello')
        await say_after(2, 'world')
    
        print(f"finished at {time.strftime('%X')}")
    
    asyncio.run(main())
    

    Förväntad utgång:

    började kl 17:13:52
    hej
    värld
    avslutat kl. 17:13:55
    
  • Funktionen asyncio.create_task() för att köra coroutines samtidigt som asyncio Tasks.

    Låt oss modifiera exemplet ovan och köra två say_after coroutines samtidigt:

    async def main():
        uppgift1 = asyncio.create_task(
            say_after(1, 'hej'))
    
        uppgift2 = asyncio.create_task(
            say_after(2, 'värld'))
    
        print(f"startade vid {time.strftime('%X')}")
    
        # Vänta tills båda uppgifterna är slutförda (bör ta
        # cirka 2 sekunder.)
        vänta uppgift1
        vänta uppgift2
    
        print(f"klar vid {time.strftime('%X')}")
    

    Observera att den förväntade utdata nu visar att utdraget körs 1 sekund snabbare än tidigare:

    började kl 17:14:32
    hej
    värld
    avslutat kl. 17:14:34
    
  • Klassen asyncio.TaskGroup ger ett mer modernt alternativ till create_task(). Med hjälp av detta API blir det sista exemplet:

    async def main():
        async med asyncio.TaskGroup() som tg:
            uppgift1 = tg.create_task(
                say_after(1, 'hej'))
    
            uppgift2 = tg.create_task(
                say_after(2, 'värld'))
    
            print(f"startade vid {time.strftime('%X')}")
    
        # Await är implicit när kontexthanteraren avslutas.
    
        print(f"klar vid {time.strftime('%X')}")
    

    Tidpunkten och resultatet ska vara detsamma som för den tidigare versionen.

    Tillagd i version 3.11: asyncio.TaskGroup.

Förväntansfulla

Vi säger att ett objekt är ett awaitable-objekt om det kan användas i ett await-uttryck. Många asyncio API:er är utformade för att acceptera awaitables.

Det finns tre huvudtyper av awaitable-objekt: Koroutiner, Uppgifter och Futures.

Coroutines

Python coroutines är awaitables och kan därför väntas från andra coroutines:

import asyncio

async def nested():
    return 42

async def main():
    # Nothing happens if we just call "nested()".
    # A coroutine object is created but not awaited,
    # so it *won't run at all*.
    nested()  # will raise a "RuntimeWarning".

    # Let's do it differently now and await it:
    print(await nested())  # will print "42".

asyncio.run(main())

Viktigt

I den här dokumentationen kan termen ”coroutine” användas för två närbesläktade begrepp:

  • en coroutine-funktion: en async def-funktion;

  • ett koroutinobjekt: ett objekt som returneras genom anrop av en koroutinfunktion.

Uppgifter

Tasks används för att schemalägga coroutines samtidigt.

När en coroutine paketeras in i en Task med funktioner som asyncio.create_task() schemaläggs coroutinen automatiskt för att köras snart:

import asyncio

async def nested():
    return 42

async def main():
    # Schedule nested() to run soon concurrently
    # with "main()".
    task = asyncio.create_task(nested())

    # "task" can now be used to cancel "nested()", or
    # can simply be awaited to wait until it is complete:
    await task

asyncio.run(main())

Terminskontrakt

En Future är ett speciellt lågnivå väntande objekt som representerar ett eventuellt resultat av en asynkron operation.

När ett Future-objekt är awaited betyder det att coroutinen kommer att vänta tills Future-objektet har lösts upp på något annat ställe.

Framtida objekt i asyncio behövs för att göra det möjligt att använda callback-baserad kod med async/await.

Normalt finns det inget behov av att skapa Future-objekt på applikationsnivå.

Framtida objekt, som ibland exponeras av bibliotek och vissa asyncio API:er, kan inväntas:

async def main():
    await function_that_returns_a_future_object()

    # detta är också giltigt:
    await asyncio.gather(
        function_that_returns_a_future_object(),
        någon_python_koroutin()
    )

Ett bra exempel på en lågnivåfunktion som returnerar ett Future-objekt är loop.run_in_executor().

Skapa uppgifter

Källkod: Lib/asyncio/tasks.py


asyncio.create_task(coro, *, name=None, context=None, eager_start=None, **kwargs)

Packa in coro coroutine i en Task och schemalägg dess exekvering. Returnera Task-objektet.

Den fullständiga funktionssignaturen är i stort sett densamma som för Task-konstruktören (eller fabriken) - alla nyckelordsargument till denna funktion skickas vidare till det gränssnittet.

Ett valfritt argument context med endast nyckelord gör det möjligt att ange en anpassad contextvars.Context som coro ska köras i. Den aktuella kontextkopian skapas när ingen kontext anges.

Ett valfritt argument eager_start, som endast innehåller nyckelord, gör det möjligt att ange om uppgiften ska utföras ivrigt under anropet till create_task eller schemaläggas senare. Om eager_start inte anges kommer det läge som anges av loop.set_task_factory() att användas.

Uppgiften utförs i den loop som returneras av get_running_loop(), RuntimeError uppstår om det inte finns någon loop i den aktuella tråden.

Anteckning

asyncio.TaskGroup.create_task() är ett nytt alternativ som utnyttjar strukturell samtidighet; det gör det möjligt att vänta på en grupp relaterade uppgifter med starka säkerhetsgarantier.

Viktigt

Spara en referens till resultatet av denna funktion för att undvika att en uppgift försvinner mitt under utförandet. Händelseslingan behåller bara svaga referenser till uppgifter. En uppgift som inte refereras någon annanstans kan när som helst bli skräpinsamlad, till och med innan den är klar. För tillförlitliga ”skjut-och-glöm”-bakgrundsuppgifter, samla dem i en samling:

background_tasks = set()

for i in range(10):
    task = asyncio.create_task(some_coro(param=i))

    # Add task to the set. This creates a strong reference.
    background_tasks.add(task)

    # To prevent keeping references to finished tasks forever,
    # make each task remove its own reference from the set after
    # completion:
    task.add_done_callback(background_tasks.discard)

Tillagd i version 3.7.

Ändrad i version 3.8: Parametern name har lagts till.

Ändrad i version 3.11: Parametern context har lagts till.

Ändrad i version 3.14: Lade till parametern eager_start genom att skicka vidare alla kwargs.

Avbokning av uppdrag

Uppgifter kan enkelt och säkert avbrytas. När en uppgift avbryts kommer asyncio.CancelledError att skapas i uppgiften vid nästa tillfälle.

Det rekommenderas att coroutines använder try/finally block för att robust utföra rensningslogik. Om asyncio.CancelledError fångas explicit, bör det i allmänhet spridas när upprensningen är klar. asyncio.CancelledError underklassar direkt BaseException så den mesta koden behöver inte vara medveten om det.

De asynciokomponenter som möjliggör strukturerad samtidighet, som asyncio.TaskGroup och asyncio.timeout(), implementeras med hjälp av annullering internt och kan bete sig illa om en coroutine sväljer asyncio.CancelledError. På samma sätt bör användarkod i allmänhet inte anropa uncancel. Men i de fall då det verkligen är önskvärt att undertrycka asyncio.CancelledError, är det nödvändigt att också anropa uncancel() för att helt ta bort annulleringstillståndet.

Arbetsgrupper

Uppgiftsgrupper kombinerar ett API för skapande av uppgifter med ett bekvämt och tillförlitligt sätt att vänta på att alla uppgifter i gruppen ska bli klara.

class asyncio.TaskGroup

En asynchronous context manager som innehåller en grupp av uppgifter. Uppgifter kan läggas till i gruppen med create_task(). Alla uppgifter är väntade när kontexthanteraren avslutas.

Tillagd i version 3.11.

create_task(coro, *, name=None, context=None, eager_start=None, **kwargs)

Skapa en uppgift i denna uppgiftsgrupp. Signaturen matchar den för asyncio.create_task(). Om uppgiftsgruppen är inaktiv (t.ex. ännu inte påbörjad, redan avslutad eller håller på att stängas ner), stänger vi den givna coro.

Ändrad i version 3.13: Stäng den givna coroutinen om uppgiftsgruppen inte är aktiv.

Ändrad i version 3.14: Vidarebefordrar alla kwargs till loop.create_task()

Exempel:

async def main():
    async med asyncio.TaskGroup() som tg:
        uppgift1 = tg.create_task(någon_coro(...))
        uppgift2 = tg.create_task(en annan_coro(...))
    print(f"Båda uppgifterna har slutförts nu: {task1.result()}, {task2.result()}")

Satsen async with väntar på att alla uppgifter i gruppen ska bli klara. Under väntetiden kan nya uppgifter fortfarande läggas till i gruppen (till exempel genom att skicka tg till en av coroutinerna och anropa tg.create_task() i den coroutinen). När den sista uppgiften har slutförts och async with-blocket har avslutats, kan inga nya uppgifter läggas till i gruppen.

Första gången någon av uppgifterna i gruppen misslyckas med ett annat undantag än asyncio.CancelledError, avbryts de återstående uppgifterna i gruppen. Inga ytterligare uppgifter kan då läggas till i gruppen. Om kroppen av async with -satsen fortfarande är aktiv (dvs. __aexit__() har ännu inte anropats) avbryts även den uppgift som direkt innehåller async with -satsen. Det resulterande asyncio.CancelledError avbryter ett await, men det sprider sig inte utanför det omgivande async with -uttrycket.

När alla uppgifter har slutförts, om några uppgifter har misslyckats med ett annat undantag än asyncio.CancelledError, kombineras dessa undantag i en ExceptionGroup eller BaseExceptionGroup (beroende på vad som är lämpligt; se deras dokumentation) som sedan tas upp.

Två basundantag behandlas speciellt: Om någon uppgift misslyckas med KeyboardInterrupt eller SystemExit, avbryter uppgiftsgruppen fortfarande de återstående uppgifterna och väntar på dem, men då återkallas den ursprungliga KeyboardInterrupt eller SystemExit istället för ExceptionGroup eller BaseExceptionGroup.

Om async with-satsens kropp avslutas med ett undantag (så att __aexit__() anropas med en undantagsuppsättning), behandlas detta på samma sätt som om en av uppgifterna misslyckades: de återstående uppgifterna avbryts och väntar sedan, och undantag som inte avbryts grupperas i en undantagsgrupp och tas upp. Undantaget som skickas in i __aexit__(), om det inte är asyncio.CancelledError, ingår också i undantagsgruppen. Samma specialfall görs för KeyboardInterrupt och SystemExit som i föregående stycke.

Uppgiftsgrupper är noga med att inte blanda ihop den interna annullering som används för att ”väcka” deras __aexit__() med andra parters annulleringsbegäran för den uppgift där de körs. I synnerhet när en uppgiftsgrupp är syntaktiskt nästlad i en annan, och båda upplever ett undantag i en av sina underordnade uppgifter samtidigt, kommer den inre uppgiftsgruppen att behandla sina undantag, och sedan kommer den yttre uppgiftsgruppen att få en annan annullering och behandla sina egna undantag.

I det fall en uppgiftsgrupp avbryts externt och även måste ge upphov till en ExceptionGroup, kommer den att anropa den överordnade uppgiftens cancel()-metod. Detta säkerställer att ett asyncio.CancelledError kommer att uppstå vid nästa await, så att annulleringen inte går förlorad.

Uppgiftsgrupper behåller antalet avbokningar som rapporteras av asyncio.Task.cancelling().

Ändrad i version 3.13: Förbättrad hantering av samtidiga interna och externa avbeställningar och korrekt bevarande av antalet avbeställningar.

Avsluta en arbetsgrupp

Även om standardbiblioteket inte har något inbyggt stöd för att avsluta en uppgiftsgrupp, kan man göra det genom att lägga till en undantagshöjande uppgift i uppgiftsgruppen och ignorera det undantag som uppstått:

import asyncio
from asyncio import TaskGroup

class TerminateTaskGroup(Exception):
    """Exception raised to terminate a task group."""

async def force_terminate_task_group():
    """Used to force termination of a task group."""
    raise TerminateTaskGroup()

async def job(task_id, sleep_time):
    print(f'Task {task_id}: start')
    await asyncio.sleep(sleep_time)
    print(f'Task {task_id}: done')

async def main():
    try:
        async with TaskGroup() as group:
            # spawn some tasks
            group.create_task(job(1, 0.5))
            group.create_task(job(2, 1.5))
            # sleep for 1 second
            await asyncio.sleep(1)
            # add an exception-raising task to force the group to terminate
            group.create_task(force_terminate_task_group())
    except* TerminateTaskGroup:
        pass

asyncio.run(main())

Förväntad utgång:

Uppgift 1: start
Uppgift 2: start
Uppgift 1: klar

Sovande

async asyncio.sleep(delay, result=None)

Blockera i fördröjning sekunder.

Om result anges returneras det till den som anropar när coroutinen avslutas.

sleep() avbryter alltid den aktuella uppgiften, så att andra uppgifter kan köras.

Om du ställer in fördröjningen till 0 får du en optimerad väg så att andra uppgifter kan köras. Detta kan användas av funktioner som körs under lång tid för att undvika att blockera händelseslingan under hela funktionsanropet.

Exempel på coroutine som visar aktuellt datum varannan sekund i 5 sekunder:

import asyncio
import datetime

async def display_date():
    loop = asyncio.get_running_loop()
    end_time = loop.time() + 5.0
    medan True:
        print(datetime.datetime.now())
        if (loop.time() + 1.0) >= end_time:
            break
        await asyncio.sleep(1)

asyncio.run(display_date())

Ändrad i version 3.10: Parametern loop har tagits bort.

Ändrad i version 3.13: Utlöser ValueError om delay är nan.

Köra uppgifter samtidigt

awaitable asyncio.gather(*aws, return_exceptions=False)

Kör awaitable objects i aws-sekvensen samtidigt.

Om någon awaitable i aws är en coroutine schemaläggs den automatiskt som en Task.

Om alla awaitables har slutförts framgångsrikt är resultatet en aggregerad lista över returnerade värden. Ordningen på resultatvärdena motsvarar ordningen på awaitables i aws.

Om return_exceptions är False (standard), kommer det första undantaget som uppstår omedelbart att överföras till den uppgift som väntar på gather(). Andra awaitables i aws-sekvensen avbryts inte och fortsätter att köras.

Om return_exceptions är True, behandlas undantag på samma sätt som lyckade resultat och sammanställs i resultatlistan.

Om gather() är avbruten, är alla inskickade awaitables (som inte har slutförts ännu) också avbrutna.

Om någon uppgift eller framtid från aws-sekvensen annulleras, behandlas den som om den gav upphov till CancelledError – anropet gather() annulleras inte i detta fall. Detta för att förhindra att avbrytandet av en inskickad Task/Future orsakar avbrytande av andra Tasks/Futures.

Anteckning

Ett nytt alternativ för att skapa och köra uppgifter samtidigt och vänta på att de ska slutföras är asyncio.TaskGroup. TaskGroup ger starkare säkerhetsgarantier än gather för schemaläggning av en nestning av underuppgifter: om en uppgift (eller en underuppgift, en uppgift som schemaläggs av en uppgift) ger upphov till ett undantag kommer TaskGroup, medan gather inte gör det, att avbryta de återstående schemalagda uppgifterna).

Exempel:

import asyncio

async def factorial(name, number):
    f = 1
    for i in range(2, number + 1):
        print(f"Task {name}: Compute factorial({number}), currently i={i}...")
        await asyncio.sleep(1)
        f *= i
    print(f"Task {name}: factorial({number}) = {f}")
    return f

async def main():
    # Schedule three calls *concurrently*:
    L = await asyncio.gather(
        factorial("A", 2),
        factorial("B", 3),
        factorial("C", 4),
    )
    print(L)

asyncio.run(main())

# Expected output:
#
#     Task A: Compute factorial(2), currently i=2...
#     Task B: Compute factorial(3), currently i=2...
#     Task C: Compute factorial(4), currently i=2...
#     Task A: factorial(2) = 2
#     Task B: Compute factorial(3), currently i=3...
#     Task C: Compute factorial(4), currently i=3...
#     Task B: factorial(3) = 6
#     Task C: Compute factorial(4), currently i=4...
#     Task C: factorial(4) = 24
#     [2, 6, 24]

Anteckning

Om return_exceptions är false, kommer en avbrytning av gather() efter att den har markerats som utförd inte att avbryta några inlämnade awaitables. Till exempel kan gather markeras som utförd efter att ett undantag har spridits till den som anropar, därför kommer inte anrop av gather.cancel() efter att ett undantag har fångats upp (av en av awaitables) från gather att avbryta någon annan awaitable.

Ändrad i version 3.7: Om gather själv avbryts, sprids avbrottet oavsett return_exceptions.

Ändrad i version 3.10: Parametern loop har tagits bort.

Föråldrad sedan version 3.10: Deprecation-varning utfärdas om inga positionella argument anges eller om inte alla positionella argument är Future-liknande objekt och det inte finns någon pågående händelseslinga.

Eager Task Factory

asyncio.eager_task_factory(loop, coro, *, name=None, context=None)

En uppgiftsfabrik för ivrig exekvering av uppgifter.

När denna fabrik används (via loop.set_task_factory(asyncio.eager_task_factory)), börjar coroutines exekveras synkront under Task-konstruktionen. Uppgifterna schemaläggs endast i händelseslingan om de blockeras. Detta kan förbättra prestandan eftersom overhead för schemaläggning av loopar undviks för coroutines som slutförs synkront.

Ett vanligt exempel där detta är fördelaktigt är coroutines som använder caching eller memoization för att undvika faktisk I/O när det är möjligt.

Anteckning

Omedelbar exekvering av coroutinen är en semantisk förändring. Om coroutinen returnerar eller höjer, schemaläggs aldrig uppgiften till händelseslingan. Om coroutine-exekveringen blockeras schemaläggs uppgiften till händelseslingan. Denna förändring kan medföra beteendeförändringar i befintliga applikationer. Till exempel kommer applikationens exekveringsordning för uppgifter sannolikt att ändras.

Tillagd i version 3.12.

asyncio.create_eager_task_factory(custom_task_constructor)

Skapa en fabrik för ivriga uppgifter, liknande eager_task_factory(), som använder den medföljande custom_task_constructor när en ny uppgift skapas istället för standard Task.

custom_task_constructor måste vara en kallbar med en signatur som matchar signaturen för Task.__init__. Anropsbarheten måste returnera ett asyncio.Task-kompatibelt objekt.

Denna funktion returnerar en kallbar som är avsedd att användas som en uppgiftsfabrik för en händelseslinga via loop.set_task_factory(factory)).

Tillagd i version 3.12.

Avskärmning från annullering

awaitable asyncio.shield(aw)

Skydda ett awaitable object från att bli cancelled.

Om aw är en coroutine schemaläggs den automatiskt som en Task.

Uttalandet:

uppgift = asyncio.create_task(något())
res = await shield(uppgift)

är likvärdig med:

res = vänta  något()

utom att om coroutinen som innehåller den avbryts, avbryts inte den Task som körs i något(). Ur något() synvinkel skedde inte avbrytandet. Även om dess anropare fortfarande avbryts, så ger ”await”-uttrycket fortfarande upphov till ett CancelledError.

Om något() upphävs på annat sätt (dvs. inifrån sig själv) skulle det också upphäva sköld().

Om man vill ignorera annulleringen helt (rekommenderas inte) bör funktionen shield() kombineras med en try/except-sats enligt följande:

uppgift = asyncio.create_task(något())
försök:
    res = await shield(uppgift)
except CancelledError:
    res = Ingen

Viktigt

Spara en referens till uppgifter som skickas till denna funktion, för att undvika att en uppgift försvinner mitt under utförandet. Händelseslingan behåller bara svaga referenser till uppgifter. En uppgift som inte refereras någon annanstans kan när som helst bli skräpinsamlad, även innan den är klar.

Ändrad i version 3.10: Parametern loop har tagits bort.

Föråldrad sedan version 3.10: Deprecation-varning utfärdas om aw inte är ett Future-like-objekt och det inte finns någon pågående händelseslinga.

Tidsfrister

asyncio.timeout(delay)

Returnerar en asynkron kontexthanterare som kan användas för att begränsa den tid det tar att vänta på något.

delay kan antingen vara None, eller ett float/int antal sekunder att vänta. Om delay är None kommer ingen tidsgräns att tillämpas; detta kan vara användbart om fördröjningen är okänd när kontexthanteraren skapas.

I båda fallen kan kontexthanteraren omplaneras efter skapandet med hjälp av Timeout.reschedule().

Exempel:

async def main():
    async med asyncio.timeout(10):
        await long_running_task()

Om long_running_task tar mer än 10 sekunder att slutföra kommer kontexthanteraren att avbryta den aktuella uppgiften och hantera det resulterande asyncio.CancelledError internt och omvandla det till ett TimeoutError som kan fångas upp och hanteras.

Anteckning

Kontexthanteraren asyncio.timeout() omvandlar asyncio.CancelledError till ett TimeoutError, vilket innebär att TimeoutError endast kan fångas utanför kontexthanteraren.

Exempel på att fånga TimeoutError:

async def main():
    try:
        async med asyncio.timeout(10):
            await long_running_task()
    except TimeoutError:
        print("Den långa operationen fick en timeout, men vi har hanterat det.")

    print("Detta uttalande kommer att köras oavsett.")

Den kontexthanterare som produceras av asyncio.timeout() kan schemaläggas om till en annan deadline och inspekteras.

class asyncio.Timeout(when)

En asynkron kontexthanterare för att avbryta försenade coroutines.

when ska vara en absolut tidpunkt då kontexten ska ta slut, mätt med händelseslingans klocka:

  • Om when är None kommer timeouten aldrig att utlösas.

  • Om when < loop.time(), kommer timeouten att utlösas vid nästa iteration av händelseslingan.

when() float | None

Returnerar den aktuella tidsfristen, eller None om den aktuella tidsfristen inte har angetts.

reschedule(when: float | None)

Flytta fram timeouten.

expired() bool

Returnerar om kontexthanteraren har överskridit sin tidsfrist (expired).

Exempel:

async def main():
    try:
        # Vi vet inte timeouten när vi startar, så vi skickar ``None``.
        async med asyncio.timeout(None) som cm:
            # Vi känner till tidsgränsen nu, så vi flyttar den.
            new_deadline = get_running_loop().time() + 10
            cm.reschedule(ny_deadline)

            await long_running_task()
    utom TimeoutError:
        passera

    if cm.expired():
        print("Det ser ut som om vi inte blev klara i tid.")

Timeout-kontexthanterare kan säkert nästlas.

Tillagd i version 3.11.

asyncio.timeout_at(when)

Liknar asyncio.timeout(), förutom att when är den absoluta tiden för att sluta vänta, eller None.

Exempel:

async def main():
    loop = get_running_loop()
    deadline = loop.tid() + 20
    försök:
        async med asyncio.timeout_at(deadline):
            await long_running_task()
    except TimeoutError:
        print("Den långa operationen timade ut, men vi har hanterat det.")

    print("Detta uttalande kommer att köras oavsett.")

Tillagd i version 3.11.

async asyncio.wait_for(aw, timeout)

Vänta på att aw awaitable ska slutföras med en timeout.

Om aw är en coroutine schemaläggs den automatiskt som en Task.

timeout kan antingen vara None eller ett float eller int antal sekunder att vänta på. Om timeout är None, blockeras tills framtiden är klar.

Om en timeout inträffar avbryts uppgiften och TimeoutError visas.

För att undvika uppgiften cancellation, linda in den i shield().

Funktionen kommer att vänta tills framtiden faktiskt avbryts, så den totala väntetiden kan överstiga timeout. Om ett undantag inträffar under annulleringen sprids det.

Om väntan avbryts, avbryts också den framtida aw.

Exempel:

async def evighet():
    # Sova i en timme
    await asyncio.sleep(3600)
    print('yay!')

async def main():
    # Vänta i högst 1 sekund
    försök:
        await asyncio.wait_for(evighet(), timeout=1.0)
    except TimeoutError:
        print('timeout!')

asyncio.run(main())

# Förväntad utgång:
#
# timeout!

Ändrad i version 3.7: När aw avbryts på grund av en timeout, väntar wait_for på att aw ska avbrytas. Tidigare gav det upphov till TimeoutError omedelbart.

Ändrad i version 3.10: Parametern loop har tagits bort.

Ändrad i version 3.11: Utlöser TimeoutError istället för asyncio.TimeoutError.

Primitiva väntetider

async asyncio.wait(aws, *, timeout=None, return_when=ALL_COMPLETED)

Kör Future och Task-instanser i aws-iterabeln samtidigt och blockera till det villkor som anges av return_when.

aws-iterabeln får inte vara tom.

Returnerar två uppsättningar av Tasks/Futures: (utförd, väntande).

Användning:

done, pending = await asyncio.wait(aws)

timeout (en float eller int), om den anges, kan användas för att styra det maximala antalet sekunder som ska väntas innan återgång.

Observera att denna funktion inte ger upphov till TimeoutError. Futures eller Tasks som inte är klara när timeouten inträffar returneras helt enkelt i den andra uppsättningen.

return_when anger när denna funktion ska returnera. Den måste vara en av följande konstanter:

Konstant

Beskrivning

asyncio.FIRST_COMPLETED

Funktionen kommer att återkomma när någon framtid avslutas eller avbryts.

asyncio.FIRST_EXCEPTION

Funktionen återkommer när en framtid avslutas genom att ett undantag utlöses. Om ingen framtid ger upphov till ett undantag så är det likvärdigt med ALL_COMPLETED.

asyncio.ALL_COMPLETED

Funktionen återkommer när alla terminer är avslutade eller avbrutna.

Till skillnad från wait_for() avbryter inte wait() futures när en timeout inträffar.

Ändrad i version 3.10: Parametern loop har tagits bort.

Ändrad i version 3.11: Det är förbjudet att skicka coroutine-objekt direkt till wait().

Ändrad i version 3.12: Stöd för generatorer som ger uppgifter har lagts till.

asyncio.as_completed(aws, *, timeout=None)

Kör awaitable objects i aws-iterabeln samtidigt. Det returnerade objektet kan itereras för att få resultaten från awaitables när de avslutas.

Objektet som returneras av as_completed() kan itereras som en asynchronous iterator eller en vanlig iterator. När asynkron iteration används, blir de ursprungligen levererade awaitables yielded om de är tasks eller futures. Detta gör det enkelt att korrelera tidigare schemalagda uppgifter med deras resultat. Exempel:

ipv4_connect = create_task(open_connection("127.0.0.1", 80))
ipv6_connect = create_task(open_connection("::1", 80))
uppgifter = [ipv4_connect, ipv6_connect]

async for earliest_connect in as_completed(tasks):
    # earliest_connect är klar. Resultatet kan erhållas genom att
    # vänta på det eller anropa earliest_connect.result()
    reader, writer = vänta  earliest_connect

    om earliest_connect är ipv6_connect:
        print("IPv6-anslutning upprättad.")
    annat:
        print("IPv4-anslutning upprättad.")

Under asynkron iteration kommer implicit skapade uppgifter att ges för levererade awaitables som inte är uppgifter eller futures.

När den används som en vanlig iterator ger varje iteration en ny coroutine som returnerar resultatet eller väcker undantaget för nästa slutförda awaitable. Detta mönster är kompatibelt med Python-versioner äldre än 3.13:

ipv4_connect = create_task(open_connection("127.0.0.1", 80))
ipv6_connect = create_task(open_connection("::1", 80))
uppgifter = [ipv4_connect, ipv6_connect]

for next_connect in as_completed(tasks):
    # next_connect är inte ett av de ursprungliga uppgiftsobjekten. Det måste vara
    # väntas för att få fram resultatvärdet eller väcka undantag för den
    # awaitable som avslutas härnäst.
    reader, writer = vänta  next_connect

Ett TimeoutError uppstår om timeouten inträffar innan alla awaitables är klara. Detta orsakas av async for-loopen under asynkron iteration eller av de coroutines som uppstår under vanlig iteration.

Ändrad i version 3.10: Parametern loop har tagits bort.

Föråldrad sedan version 3.10: Deprecation-varning utfärdas om inte alla awaitable-objekt i aws-iterabeln är Future-liknande objekt och det inte finns någon pågående händelseslinga.

Ändrad i version 3.12: Stöd för generatorer som ger uppgifter har lagts till.

Ändrad i version 3.13: Resultatet kan nu användas som antingen en asynchronous iterator eller som en vanlig iterator (tidigare var det bara en vanlig iterator).

Löpande i trådar

async asyncio.to_thread(func, /, *args, **kwargs)

Asynkron körning av funktionen func i en separat tråd.

Alla *args och **kwargs som anges för denna funktion skickas direkt till func. Dessutom sprids den aktuella contextvars.Context, vilket gör att kontextvariabler från händelseslingans tråd kan nås i den separata tråden.

Returnera en coroutine som kan inväntas för att få det slutliga resultatet av func.

Denna coroutine-funktion är främst avsedd att användas för att köra IO-bundna funktioner/metoder som annars skulle blockera händelseslingan om de kördes i huvudtråden. Till exempel:

def blocking_io():
    print(f"starta blockering_io vid {time.strftime('%X')}")
    # Observera att time.sleep() kan ersättas med vilken blockerande
    # IO-bunden operation, t.ex. filoperationer.
    time.sleep(1)
    print(f"blockering_io slutförd vid {time.strftime('%X')}")

async def main():
    print(f"startade main vid {time.strftime('%X')}")

    await asyncio.gather(
        asyncio.to_thread(blocking_io),
        asyncio.sleep(1))

    print(f"avslutade huvudtråden vid {time.strftime('%X')}")


asyncio.run(main())

# Förväntad utgång:
#
# startade main kl 19:50:53
# start blocking_io at 19:50:53
# blocking_io complete at 19:50:54
# finished main at 19:50:54

Att direkt anropa blocking_io() i en coroutine skulle blockera händelseslingan under hela dess varaktighet, vilket resulterar i ytterligare 1 sekunds körtid. Genom att istället använda asyncio.to_thread() kan vi köra den i en separat tråd utan att blockera händelseslingan.

Anteckning

På grund av GIL kan asyncio.to_thread() vanligtvis bara användas för att göra IO-bundna funktioner icke-blockerande. Men för tilläggsmoduler som släpper GIL eller alternativa Python-implementationer som inte har någon, kan asyncio.to_thread() även användas för CPU-bundna funktioner.

Tillagd i version 3.9.

Schemaläggning från andra trådar

asyncio.run_coroutine_threadsafe(coro, loop)

Skicka en coroutine till den angivna händelseslingan. Tråd-säker.

Returnera en concurrent.futures.Future för att vänta på resultatet från en annan OS-tråd.

Denna funktion är avsedd att anropas från en annan OS-tråd än den där händelseslingan körs. Exempel:

def in_thread(loop: asyncio.AbstractEventLoop) -> None:
    # Kör lite blockerande IO
    pathlib.Path("exempel.txt").write_text("hallå världen", encoding="utf8")

    # Skapa en coroutine
    coro = asyncio.sleep(1, resultat=3)

    # Skicka coroutinen till en given loop
    future = asyncio.run_coroutine_threadsafe(coro, loop)

    # Vänta på resultatet med ett valfritt timeout-argument
    assert future.result(timeout=2) == 3

async def amain() -> Ingen:
    # Hämta den pågående slingan
    loop = asyncio.get_running_loop()

    # Kör något i en tråd
    await asyncio.to_thread(in_thread, loop)

Det är också möjligt att köra tvärtom. Exempel:

@kontextlib.kontexthanterare
def loop_in_thread() -> Generator[asyncio.AbstractEventLoop]:
    loop_fut = concurrent.futures.Future[asyncio.AbstractEventLoop]()
    stop_event = asyncio.Event()

    async def main() -> None:
        loop_fut.set_result(asyncio.get_running_loop())
        await stop_event.wait()

    med concurrent.futures.ThreadPoolExecutor(1) som tpe:
        complete_fut = tpe.submit(asyncio.run, main())
        for fut in concurrent.futures.as_completed((loop_fut, complete_fut)):
            if fut is loop_fut:
                loop = loop_fut.result()
                försök:
                    ge loop
                till sist:
                    loop.call_soon_threadsafe(stop_event.set)
            else:
                fut.result()

# Skapa en slinga i en annan tråd
med loop_in_thread() som loop:
    # Skapa en coroutine
    coro = asyncio.sleep(1, resultat=3)

    # Skicka coroutinen till en given slinga
    future = asyncio.run_coroutine_threadsafe(coro, loop)

    # Vänta på resultatet med ett valfritt timeout-argument
    assert future.result(timeout=2) == 3

Om ett undantag inträffar i coroutinen kommer den returnerade Future att meddelas. Den kan också användas för att avbryta uppgiften i händelseslingan:

försök:
    resultat = future.result(timeout)
except TimeoutError:
    print('Coroutinen tog för lång tid och avbröt uppgiften...')
    future.cancel()
except Exception as exc:
    print(f'Coroutinen tog upp ett undantag: {exc!r}')
else:
    print(f'Coroutinen returnerade: {result!r}')

Se concurrency and multithreading i dokumentationen.

Till skillnad från andra asynciofunktioner kräver den här funktionen att argumentet loop skickas explicit.

Tillagd i version 3.5.1.

Introspektion

asyncio.current_task(loop=None)

Returnerar den instans av Task som körs för tillfället, eller None om ingen uppgift körs.

Om loop är None används get_running_loop() för att hämta den aktuella loopen.

Tillagd i version 3.7.

asyncio.all_tasks(loop=None)

Returnerar en uppsättning ännu inte avslutade Task-objekt som körs av slingan.

Om loop är None, används get_running_loop() för att hämta aktuell loop.

Tillagd i version 3.7.

asyncio.iscoroutine(obj)

Returnerar True om obj är ett coroutine-objekt.

Tillagd i version 3.4.

Uppgiftsobjekt

class asyncio.Task(coro, *, loop=None, name=None, context=None, eager_start=False)

Ett Future-liknande-objekt som kör en Python coroutine. Inte tråd-säkert.

Tasks används för att köra coroutines i händelseslingor. Om en coroutine väntar på en Future, avbryter Task exekveringen av coroutinen och väntar på att Future ska slutföras. När framtiden är färdig återupptas exekveringen av den omslutna coroutinen.

Eventloops använder kooperativ schemaläggning: en eventloop kör en Task åt gången. Medan en Task väntar på att en Future ska slutföras, kör eventloopen andra Tasks, callbacks eller utför IO-operationer.

Använd högnivåfunktionen asyncio.create_task() för att skapa Tasks, eller lågnivåfunktionerna loop.create_task() eller ensure_future(). Manuell instansiering av Tasks avråds.

För att avbryta en pågående Task används metoden cancel(). Om du anropar den kommer uppgiften att kasta ett CancelledError undantag i den omslutna coroutinen. Om en coroutine väntar på ett Future-objekt under avbrytandet, kommer Future-objektet att avbrytas.

cancelled() kan användas för att kontrollera om uppgiften avbröts. Metoden returnerar True om den inlindade coroutinen inte undertryckte CancelledError undantaget och faktiskt avbröts.

asyncio.Task ärver från Future alla dess API:er utom Future.set_result() och Future.set_exception().

Ett valfritt argument context med endast nyckelord gör det möjligt att ange en anpassad contextvars.Context som coro ska köras i. Om ingen kontext anges kopierar uppgiften den aktuella kontexten och kör senare sin coroutine i den kopierade kontexten.

Ett valfritt argument eager_start som endast innehåller nyckelord gör det möjligt att ivrigt starta exekveringen av asyncio.Task när uppgiften skapas. Om värdet är satt till True och händelseslingan körs, kommer uppgiften att börja exekvera coroutinen omedelbart, tills första gången coroutinen blockerar. Om coroutinen returnerar eller höjs utan att blockera, kommer uppgiften att slutföras ivrigt och hoppa över schemaläggningen till händelseslingan.

Ändrad i version 3.7: Lagt till stöd för modulen contextvars.

Ändrad i version 3.8: Parametern name har lagts till.

Föråldrad sedan version 3.10: Deprecation-varning utfärdas om loop inte anges och det inte finns någon pågående händelseslinga.

Ändrad i version 3.11: Parametern context har lagts till.

Ändrad i version 3.12: Parametern eager_start har lagts till.

done()

Returnerar True om uppgiften är done.

En Task är färdig när den omslutna coroutinen antingen returnerade ett värde, utlöste ett undantag eller när Task avbröts.

result()

Returnera resultatet av uppgiften.

Om uppgiften är utförd returneras resultatet av den omslutna coroutinen (eller om coroutinen gav upphov till ett undantag, ges det undantaget upp igen)

Om uppgiften har avbrutits ger den här metoden upphov till ett CancelledError-undantag.

Om uppgiftens resultat ännu inte är tillgängligt ger denna metod upphov till ett InvalidStateError-undantag.

exception()

Returnera undantaget för uppgiften.

Om den inkapslade coroutinen gav upphov till ett undantag returneras undantaget. Om den inkapslade coroutinen returnerades normalt returnerar denna metod None.

Om uppgiften har avbrutits ger den här metoden upphov till ett CancelledError-undantag.

Om uppgiften inte är färdig ännu, ger denna metod upphov till ett InvalidStateError-undantag.

add_done_callback(callback, *, context=None)

Lägg till en återuppringning som ska köras när uppgiften är klar.

Denna metod bör endast användas i kod som bygger på återuppringning på låg nivå.

Se dokumentationen av Future.add_done_callback() för mer information.

remove_done_callback(callback)

Ta bort callback från listan över callbacks.

Denna metod bör endast användas i kod som bygger på återuppringning på låg nivå.

Se dokumentationen av Future.remove_done_callback() för mer information.

get_stack(*, limit=None)

Returnera listan med stapelramar för denna Task.

Om den inkapslade coroutinen inte är klar returneras den stack där den är avbruten. Om coroutinen har slutförts framgångsrikt eller avbrutits, returneras en tom lista. Om coroutinen avslutades av ett undantag returneras listan med spårningsramar.

Ramarna är alltid sorterade från äldst till nyast.

Endast en stackram returneras för en avbruten coroutine.

Det valfria argumentet limit anger det maximala antalet ramar som ska returneras; som standard returneras alla tillgängliga ramar. Ordningen på listan som returneras skiljer sig åt beroende på om en stack eller en traceback returneras: de nyaste ramarna i en stack returneras, men de äldsta ramarna i en traceback returneras. (Detta motsvarar beteendet hos traceback-modulen)

print_stack(*, limit=None, file=None)

Skriv ut stacken eller traceback för den här uppgiften.

Detta ger liknande utdata som traceback-modulen för de ramar som hämtas av get_stack().

Argumentet limit skickas direkt till get_stack().

Argumentet file är en I/O-ström som utdata skrivs till; som standard skrivs utdata till sys.stdout.

get_coro()

Returnerar coroutine-objektet som omsluts av Task.

Anteckning

Detta kommer att returnera None för uppgifter som redan har slutförts ivrigt. Se Eager Task Factory.

Tillagd i version 3.8.

Ändrad i version 3.12: Nyligen tillagd ivrig uppgiftsutförande innebär att resultatet kan vara None.

get_context()

Returnerar contextvars.Context-objektet som är associerat med uppgiften.

Tillagd i version 3.12.

get_name()

Returnera namnet på uppgiften.

Om inget namn uttryckligen har tilldelats uppgiften genererar standardimplementeringen av asyncio-uppgiften ett standardnamn under instantiering.

Tillagd i version 3.8.

set_name(value)

Ange namnet på uppgiften.

Argumentet value kan vara vilket objekt som helst, som sedan konverteras till en sträng.

I standardimplementeringen av Task kommer namnet att synas i repr()-utmatningen från ett Task-objekt.

Tillagd i version 3.8.

cancel(msg=None)

Begär att uppgiften ska avbrytas.

Om uppgiften redan är utförd eller annullerad, returneras False, annars returneras True.

Metoden ser till att ett CancelledError-undantag kastas in i den omslutna coroutinen vid nästa cykel i händelseslingan.

Coroutinen har sedan en chans att städa upp eller till och med neka begäran genom att undertrycka undantaget med ett try … … … except CancelledErrorfinally block. Till skillnad från Future.cancel() garanterar därför Task.cancel() inte att uppgiften kommer att avbrytas, även om det inte är vanligt att helt undertrycka avbrytande och aktivt avråds från detta. Om coroutinen ändå bestämmer sig för att undertrycka annulleringen måste den anropa Task.uncancel() utöver att fånga upp undantaget.

Ändrad i version 3.9: Parametern msg har lagts till.

Ändrad i version 3.11: Parametern msg sprids från den avbrutna uppgiften till den som väntar på den.

Följande exempel illustrerar hur coroutines kan fånga upp begäran om annullering:

async def cancel_me():
    print('cancel_me(): före sömn')

    försök:
        # Vänta i 1 timme
        await asyncio.sleep(3600)
    except asyncio.CancelledError:
        print('cancel_me(): avbryt sömn')
        höja
    slutligen:
        print('cancel_me(): efter sömn')

async def main():
    # Skapa en "cancel_me"-uppgift
    uppgift = asyncio.create_task(cancel_me())

    # Vänta i 1 sekund
    await asyncio.sleep(1)

    uppgift.avbryt()
    försök:
        await uppgift
    except asyncio.CancelledError:
        print("main(): cancel_me är avbruten nu")

asyncio.run(main())

# Förväntad utgång:
#
# cancel_me(): före sömn
# cancel_me(): avbryta sömn
# cancel_me(): efter sömn
# main(): cancel_me är avbruten nu
cancelled()

Returnerar True om uppgiften är annullerad.

Uppgiften är avbruten när avbrytandet begärdes med cancel() och den omslutna coroutinen spred undantaget CancelledError som kastades in i den.

uncancel()

Minska antalet avbokningsbegäranden för den här uppgiften.

Returnerar det återstående antalet avbokningsbegäranden.

Observera att när en avbruten uppgift har utförts är ytterligare anrop till uncancel() ineffektiva.

Tillagd i version 3.11.

Den här metoden används av asyncios interna funktioner och förväntas inte användas av slutanvändarkod. I synnerhet, om en uppgift framgångsrikt avbokas, gör detta att element av strukturerad samtidighet som Arbetsgrupper och asyncio.timeout() kan fortsätta att köras, vilket isolerar avbokningen till respektive strukturerat block. Till exempel:

async def make_request_with_timeout():
    try:
        async med asyncio.timeout(1):
            # Strukturerat block som påverkas av timeouten:
            await make_request()
            await make_another_request()
    except TimeoutError:
        log("Det uppstod en timeout")
    # Yttre kod som inte påverkas av timeouten:
    await orelaterad_kod()

Medan blocken med make_request() och make_another_request() kan avbrytas på grund av timeout, bör unrelated_code() fortsätta att köras även om timeout inträffar. Detta implementeras med uncancel(). TaskGroup kontexthanterare använder uncancel() på ett liknande sätt.

Om slutanvändarkoden av någon anledning undertrycker annullering genom att fånga CancelledError, måste den anropa denna metod för att ta bort annulleringstillståndet.

När den här metoden minskar antalet avbokningar till noll, kontrollerar metoden om ett tidigare cancel()-anrop hade ordnat så att CancelledError kunde kastas in i uppgiften. Om det inte har gjorts ännu, kommer det arrangemanget att återkallas (genom att återställa den interna _must_cancel flaggan).

Ändrad i version 3.13: Ändrad för att återkalla pågående annulleringsförfrågningar när de når noll.

cancelling()

Returnerar antalet väntande avbokningsbegäranden till denna uppgift, dvs. antalet anrop till cancel() minus antalet anrop till uncancel().

Observera att om detta antal är större än noll men uppgiften fortfarande körs, kommer cancelled() fortfarande att returnera False. Detta beror på att detta antal kan sänkas genom att anropa uncancel(), vilket kan leda till att uppgiften inte avbryts trots allt om begäran om avbrytande går ner till noll.

Den här metoden används av asyncios interna funktioner och förväntas inte användas av slutanvändarkod. Se uncancel() för mer information.

Tillagd i version 3.11.