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 asyncioTasks
.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 tillcreate_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 givnacoro
.Ä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 ärnan
.
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
– anropetgather()
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 underTask
-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 standardTask
.custom_task_constructor måste vara en kallbar med en signatur som matchar signaturen för
Task.__init__
. Anropsbarheten måste returnera ettasyncio.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 på något()
utom att om coroutinen som innehåller den avbryts, avbryts inte den Task som körs i
något()
. Urnågot()
synvinkel skedde inte avbrytandet. Även om dess anropare fortfarande avbryts, så ger ”await”-uttrycket fortfarande upphov till ettCancelledError
.Om
något()
upphävs på annat sätt (dvs. inifrån sig själv) skulle det också upphävaskö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 ärNone
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 resulterandeasyncio.CancelledError
internt och omvandla det till ettTimeoutError
som kan fångas upp och hanteras.Anteckning
Kontexthanteraren
asyncio.timeout()
omvandlarasyncio.CancelledError
till ettTimeoutError
, vilket innebär attTimeoutError
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
ärNone
kommer timeouten aldrig att utlösas.Om
when < loop.time()
, kommer timeouten att utlösas vid nästa iteration av händelseslingan.
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, ellerNone
.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 ärNone
, blockeras tills framtiden är klar.Om en timeout inträffar avbryts uppgiften och
TimeoutError
visas.För att undvika uppgiften
cancellation
, linda in den ishield()
.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 tillTimeoutError
omedelbart.Ändrad i version 3.10: Parametern loop har tagits bort.
Ändrad i version 3.11: Utlöser
TimeoutError
istället förasyncio.TimeoutError
.
Primitiva väntetider¶
- async asyncio.wait(aws, *, timeout=None, return_when=ALL_COMPLETED)¶
Kör
Future
ochTask
-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 intewait()
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 på 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 på next_connect
Ett
TimeoutError
uppstår om timeouten inträffar innan alla awaitables är klara. Detta orsakas avasync 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ändaasyncio.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, kanasyncio.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, ellerNone
om ingen uppgift körs.Om loop är
None
användsget_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ändsget_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åfunktionernaloop.create_task()
ellerensure_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 ettCancelledError
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 returnerarTrue
om den inlindade coroutinen inte undertryckteCancelledError
undantaget och faktiskt avbröts.asyncio.Task
ärver frånFuture
alla dess API:er utomFuture.set_result()
ochFuture.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 tillTrue
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 returnerasTrue
.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 CancelledError
…finally
block. Till skillnad frånFuture.cancel()
garanterar därförTask.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 anropaTask.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 undantagetCancelledError
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()
ochmake_another_request()
kan avbrytas på grund av timeout, börunrelated_code()
fortsätta att köras även om timeout inträffar. Detta implementeras meduncancel()
.TaskGroup
kontexthanterare använderuncancel()
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å attCancelledError
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 tilluncancel()
.Observera att om detta antal är större än noll men uppgiften fortfarande körs, kommer
cancelled()
fortfarande att returneraFalse
. Detta beror på att detta antal kan sänkas genom att anropauncancel()
, 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.