Coroutinen und Tasks¶
Dieser Abschnitt beschreibt High-Level asyncio APIs zur Arbeit mit Coroutinen und Tasks.
Coroutinen¶
Quellcode: Lib/asyncio/coroutines.py
Coroutinen, die mit der async/await-Syntax deklariert sind, sind der bevorzugte Weg, um asyncio-Anwendungen zu schreiben. Zum Beispiel gibt der folgende Code-Schnipsel „hello“ aus, wartet 1 Sekunde und gibt dann „world“ aus.
>>> import asyncio
>>> async def main():
... print('hello')
... await asyncio.sleep(1)
... print('world')
>>> asyncio.run(main())
hello
world
Beachten Sie, dass das bloße Aufrufen einer Coroutine diese nicht zur Ausführung plant.
>>> main()
<coroutine object main at 0x1053bb7c8>
Um eine Coroutine tatsächlich auszuführen, bietet asyncio die folgenden Mechanismen:
Die Funktion
asyncio.run(), um die primäre Einstiegsfunktion „main()“ auszuführen (siehe obiges Beispiel).Awaiten auf einer Coroutine. Der folgende Code-Schnipsel gibt nach einer Wartezeit von 1 Sekunde „hello“ aus und dann nach einer *weiteren* Wartezeit von 2 Sekunden „world“.
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())
Erwartete Ausgabe
started at 17:13:52 hello world finished at 17:13:55
Die Funktion
asyncio.create_task(), um Coroutinen parallel als asyncioTasksauszuführen.Lassen Sie uns das obige Beispiel modifizieren und zwei
say_afterCoroutinen *parallel* ausführen.async def main(): task1 = asyncio.create_task( say_after(1, 'hello')) task2 = asyncio.create_task( say_after(2, 'world')) print(f"started at {time.strftime('%X')}") # Wait until both tasks are completed (should take # around 2 seconds.) await task1 await task2 print(f"finished at {time.strftime('%X')}")
Beachten Sie, dass die erwartete Ausgabe nun zeigt, dass der Schnipsel 1 Sekunde schneller läuft als zuvor.
started at 17:14:32 hello world finished at 17:14:34
Die Klasse
asyncio.TaskGroupbietet eine modernere Alternative zucreate_task(). Mit dieser API wird das letzte Beispiel zuasync def main(): async with asyncio.TaskGroup() as tg: task1 = tg.create_task( say_after(1, 'hello')) task2 = tg.create_task( say_after(2, 'world')) print(f"started at {time.strftime('%X')}") # The await is implicit when the context manager exits. print(f"finished at {time.strftime('%X')}")
Das Timing und die Ausgabe sollten mit der vorherigen Version übereinstimmen.
Hinzugefügt in Version 3.11:
asyncio.TaskGroup.
Awaitables¶
Wir sagen, dass ein Objekt ein awaitable Objekt ist, wenn es in einem await-Ausdruck verwendet werden kann. Viele asyncio APIs sind so konzipiert, dass sie awaitables akzeptieren.
Es gibt drei Haupttypen von awaitable Objekten: Coroutinen, Tasks und Futures.
Coroutinen
Python-Coroutinen sind awaitables und können daher von anderen Coroutinen awaited werden.
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())
Wichtig
In dieser Dokumentation kann der Begriff „Coroutine“ für zwei eng verwandte Konzepte verwendet werden:
eine Coroutine-Funktion: eine
async defFunktion;ein Coroutine-Objekt: ein Objekt, das durch Aufrufen einer Coroutine-Funktion zurückgegeben wird.
Tasks
Tasks werden verwendet, um Coroutinen *parallel* zu planen.
Wenn eine Coroutine mit Funktionen wie asyncio.create_task() in einen Task eingewickelt wird, wird die Coroutine automatisch bald zur Ausführung geplant.
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())
Futures
Ein Future ist ein spezielles Low-Level awaitable Objekt, das ein zukünftiges Ergebnis einer asynchronen Operation darstellt.
Wenn ein Future-Objekt awaited wird, bedeutet dies, dass die Coroutine wartet, bis das Future an anderer Stelle aufgelöst ist.
Future-Objekte in asyncio werden benötigt, um Callback-basierte Codes mit async/await verwenden zu können.
Normalerweise gibt es keinen Bedarf, Future-Objekte im Anwendungslevel-Code zu erstellen.
Future-Objekte, die manchmal von Bibliotheken und einigen asyncio APIs offengelegt werden, können awaited werden.
async def main():
await function_that_returns_a_future_object()
# this is also valid:
await asyncio.gather(
function_that_returns_a_future_object(),
some_python_coroutine()
)
Ein gutes Beispiel für eine Low-Level-Funktion, die ein Future-Objekt zurückgibt, ist loop.run_in_executor().
Tasks erstellen¶
Quellcode: Lib/asyncio/tasks.py
- asyncio.create_task(coro, *, name=None, context=None, eager_start=None, **kwargs)¶
Wickelt die coro Coroutine in einen
Taskein und plant dessen Ausführung. Gibt das Task-Objekt zurück.Die vollständige Funktionssignatur ist weitgehend identisch mit der des
Task-Konstruktors (oder Factories) – alle Schlüsselwortargumente an diese Funktion werden an diese Schnittstelle weitergeleitet.Ein optionales schlüsselwort-only context Argument erlaubt die Angabe eines benutzerdefinierten
contextvars.Contextfür die Ausführung der coro. Die aktuelle Kontextkopie wird erstellt, wenn kein context bereitgestellt wird.Ein optionales schlüsselwort-only eager_start Argument erlaubt die Angabe, ob der Task beim Aufruf von create_task sofort ausgeführt oder später geplant werden soll. Wenn eager_start nicht übergeben wird, wird der Modus verwendet, der durch
loop.set_task_factory()gesetzt wurde.Der Task wird in der von
get_running_loop()zurückgegebenen Schleife ausgeführt.RuntimeErrorwird ausgelöst, wenn in diesem Thread keine laufende Schleife vorhanden ist.Hinweis
asyncio.TaskGroup.create_task()ist eine neue Alternative, die strukturelle Konkurrenz nutzt; sie ermöglicht das Warten auf eine Gruppe verwandter Tasks mit starken Sicherheitsgarantien.Wichtig
Speichern Sie eine Referenz auf das Ergebnis dieser Funktion, um zu verhindern, dass ein Task mitten in der Ausführung verschwindet. Die Ereignisschleife hält nur schwache Referenzen auf Tasks. Ein Task, auf den sonst nirgends Bezug genommen wird, kann jederzeit vom Garbage Collector entfernt werden, noch bevor er beendet ist. Für zuverlässige „Fire-and-Forget“-Hintergrundtasks sammeln Sie sie in einer Collection.
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)
Hinzugefügt in Version 3.7.
Geändert in Version 3.8: Parameter name hinzugefügt.
Geändert in Version 3.11: Parameter context hinzugefügt.
Geändert in Version 3.14: Parameter eager_start hinzugefügt durch Weiterleitung aller kwargs.
Task-Abbruch¶
Tasks können einfach und sicher abgebrochen werden. Wenn ein Task abgebrochen wird, wird asyncio.CancelledError beim nächsten Gelegenheitsereignis im Task ausgelöst.
Es wird empfohlen, dass Coroutinen try/finally-Blöcke verwenden, um Bereinigungslogik robust durchzuführen. Falls asyncio.CancelledError explizit abgefangen wird, sollte er nach Abschluss der Bereinigung im Allgemeinen weitergegeben werden. asyncio.CancelledError ist eine direkte Unterklasse von BaseException, sodass die meisten Codes sich dessen bewusst sein müssen.
Die asyncio-Komponenten, die strukturelle Konkurrenz ermöglichen, wie asyncio.TaskGroup und asyncio.timeout(), werden intern mit Abbruch implementiert und können sich falsch verhalten, wenn eine Coroutine asyncio.CancelledError verschluckt. Ebenso sollten Benutzeroberflächen-Codes im Allgemeinen uncancel nicht aufrufen. In Fällen, in denen die Unterdrückung von asyncio.CancelledError tatsächlich gewünscht ist, ist es jedoch notwendig, auch uncancel() aufzurufen, um den Abbruchstatus vollständig zu entfernen.
Task-Gruppen¶
Task-Gruppen kombinieren eine API zur Task-Erstellung mit einer bequemen und zuverlässigen Möglichkeit, auf die Fertigstellung aller Tasks in der Gruppe zu warten.
- class asyncio.TaskGroup¶
Ein asynchroner Kontextmanager, der eine Gruppe von Tasks enthält. Tasks können der Gruppe mit
create_task()hinzugefügt werden. Alle Tasks werden awaited, wenn der Kontextmanager beendet wird.Hinzugefügt in Version 3.11.
- create_task(coro, *, name=None, context=None, eager_start=None, **kwargs)¶
Erstellt einen Task in dieser Task-Gruppe. Die Signatur entspricht der von
asyncio.create_task(). Wenn die Task-Gruppe inaktiv ist (z.B. noch nicht betreten, bereits beendet oder im Prozess des Herunterfahrens), wird die gegebenecorogeschlossen.Geändert in Version 3.13: Schließt die gegebene Coroutine, wenn die Task-Gruppe nicht aktiv ist.
Geändert in Version 3.14: Leitet alle kwargs an
loop.create_task()weiter.
Beispiel
async def main():
async with asyncio.TaskGroup() as tg:
task1 = tg.create_task(some_coro(...))
task2 = tg.create_task(another_coro(...))
print(f"Both tasks have completed now: {task1.result()}, {task2.result()}")
Die Anweisung async with wartet, bis alle Tasks in der Gruppe fertig sind. Während des Wartens können noch neue Tasks zur Gruppe hinzugefügt werden (z. B. indem tg in eine der Coroutinen übergeben und tg.create_task() in dieser Coroutine aufgerufen wird). Sobald der letzte Task beendet ist und der async with-Block verlassen wird, können keine neuen Tasks mehr zur Gruppe hinzugefügt werden.
Wenn der erste Task, der zu der Gruppe gehört, mit einer Ausnahme außer asyncio.CancelledError fehlschlägt, werden die übrigen Tasks in der Gruppe abgebrochen. Es können dann keine weiteren Tasks zur Gruppe hinzugefügt werden. Wenn zu diesem Zeitpunkt der Körper des async with-Blocks noch aktiv ist (d. h. __aexit__() noch nicht aufgerufen wurde), wird auch der Task, der den async with-Block direkt enthält, abgebrochen. Das resultierende asyncio.CancelledError unterbricht ein await, wird aber nicht aus dem umschließenden async with-Block herausblubbern.
Sobald alle Tasks abgeschlossen sind, und falls einige Tasks mit einer Ausnahme außer asyncio.CancelledError fehlgeschlagen sind, werden diese Ausnahmen in einer ExceptionGroup oder BaseExceptionGroup (je nach Bedarf; siehe deren Dokumentation) kombiniert, die dann ausgelöst wird.
Zwei Basis-Ausnahmen werden speziell behandelt: Wenn ein Task mit KeyboardInterrupt oder SystemExit fehlschlägt, bricht die Task-Gruppe die verbleibenden Tasks immer noch ab und wartet auf sie, aber dann wird die ursprüngliche KeyboardInterrupt oder SystemExit anstelle von ExceptionGroup oder BaseExceptionGroup neu ausgelöst.
Wenn der Körper des async with-Blocks mit einer Ausnahme beendet wird (sodass __aexit__() mit einer gesetzten Ausnahme aufgerufen wird), wird dies genauso behandelt, als ob einer der Tasks fehlgeschlagen wäre: Die verbleibenden Tasks werden abgebrochen und dann abgewartet, und Nicht-Abbruch-Ausnahmen werden zu einer Ausnahme-Gruppe zusammengefasst und ausgelöst. Die Ausnahme, die in __aexit__() übergeben wird, sofern sie nicht asyncio.CancelledError ist, wird ebenfalls in die Ausnahme-Gruppe aufgenommen. Der gleiche Sonderfall wird für KeyboardInterrupt und SystemExit wie im vorherigen Absatz gemacht.
Task-Gruppen mischen den internen Abbruch, der dazu dient, ihre __aexit__() „aufzuwecken“, nicht mit Abbruch-Anfragen für den Task, in dem sie laufen und die von anderen Parteien gemacht werden. Insbesondere, wenn eine Task-Gruppe in eine andere verschachtelt ist und beide gleichzeitig eine Ausnahme in einem ihrer Kind-Tasks erfahren, verarbeitet die innere Task-Gruppe ihre Ausnahmen, und dann empfängt die äußere Task-Gruppe einen weiteren Abbruch und verarbeitet ihre eigenen Ausnahmen.
Wenn eine Task-Gruppe extern abgebrochen wird und auch eine ExceptionGroup auslösen muss, wird die cancel()-Methode des Eltern-Tasks aufgerufen. Dies stellt sicher, dass ein asyncio.CancelledError beim nächsten await ausgelöst wird, sodass der Abbruch nicht verloren geht.
Task-Gruppen erhalten die Abbruch-Zählung, die von asyncio.Task.cancelling() gemeldet wird.
Geändert in Version 3.13: Verbesserte Handhabung gleichzeitiger interner und externer Abbrüche und korrekte Beibehaltung der Abbruch-Zählungen.
Beenden einer Task-Gruppe¶
Obwohl das Beenden einer Task-Gruppe von der Standardbibliothek nicht nativ unterstützt wird, kann ein Beenden erreicht werden, indem ein Ausnahmen auslösender Task zur Task-Gruppe hinzugefügt und die ausgelöste Ausnahme ignoriert wird.
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())
Erwartete Ausgabe
Task 1: start
Task 2: start
Task 1: done
Schlafen¶
- async asyncio.sleep(delay, result=None)¶
Blockiere für delay Sekunden.
Wenn result angegeben ist, wird es an den Aufrufer zurückgegeben, wenn die Coroutine abgeschlossen ist.
sleep()suspendiert immer den aktuellen Task und erlaubt anderen Tasks, ausgeführt zu werden.Das Setzen der Verzögerung auf 0 bietet einen optimierten Pfad, um anderen Tasks die Ausführung zu ermöglichen. Dies kann von lang laufenden Funktionen verwendet werden, um zu vermeiden, dass die Ereignisschleife für die gesamte Dauer des Funktionsaufrufs blockiert wird.
Beispiel einer Coroutine, die 5 Sekunden lang jede Sekunde das aktuelle Datum anzeigt
import asyncio import datetime async def display_date(): loop = asyncio.get_running_loop() end_time = loop.time() + 5.0 while True: print(datetime.datetime.now()) if (loop.time() + 1.0) >= end_time: break await asyncio.sleep(1) asyncio.run(display_date())
Geändert in Version 3.10: Das Argument loop wurde entfernt.
Geändert in Version 3.13: Löst
ValueErroraus, wenn delaynanist.
Tasks parallel ausführen¶
- awaitable asyncio.gather(*aws, return_exceptions=False)¶
Führt awaitable objects in der aws-Sequenz *parallel* aus.
Wenn ein awaitable in aws eine Coroutine ist, wird es automatisch als Task geplant.
Wenn alle awaitables erfolgreich abgeschlossen sind, ist das Ergebnis eine aggregierte Liste der zurückgegebenen Werte. Die Reihenfolge der Ergebniswerte entspricht der Reihenfolge der awaitables in aws.
Wenn return_exceptions auf
False(Standard) gesetzt ist, wird die erste ausgelöste Ausnahme sofort an den Task weitergegeben, der aufgather()wartet. Andere awaitables in der aws-Sequenz werden **nicht abgebrochen** und laufen weiter.Wenn return_exceptions auf
Truegesetzt ist, werden Ausnahmen wie erfolgreiche Ergebnisse behandelt und in der Ergebnisliste aggregiert.Wenn
gather()*abgebrochen* wird, werden alle übergebenen awaitables (die noch nicht abgeschlossen sind) ebenfalls abgebrochen.Wenn ein Task oder Future aus der aws-Sequenz *abgebrochen* wird, wird dies so behandelt, als ob es
CancelledErrorausgelöst hätte – dergather()-Aufruf wird in diesem Fall **nicht** abgebrochen. Dies soll verhindern, dass der Abbruch eines übergebenen Tasks/Futures andere Tasks/Futures abbricht.Hinweis
Eine neue Alternative zum Erstellen und Ausführen von Tasks parallel und zum Warten auf deren Abschluss ist
asyncio.TaskGroup. TaskGroup bietet stärkere Sicherheitsgarantien als gather für die Planung einer Verschachtelung von Untertasks: Wenn ein Task (oder ein Untertask, ein von einem Task geplanter Task) eine Ausnahme auslöst, bricht TaskGroup die verbleibenden geplanten Tasks ab, während gather dies nicht tut.Beispiel
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]
Hinweis
Wenn return_exceptions falsch ist, bricht das Abbrechen von gather(), nachdem es als erledigt markiert wurde, keine übergebenen awaitables ab. Zum Beispiel kann gather() als erledigt markiert werden, nachdem eine Ausnahme an den Aufrufer weitergegeben wurde. Daher bricht der Aufruf von
gather.cancel()nach dem Abfangen einer Ausnahme (die von einem der awaitables ausgelöst wurde) von gather keine anderen awaitables ab.Geändert in Version 3.7: Wenn gather() selbst abgebrochen wird, wird der Abbruch unabhängig von return_exceptions weitergegeben.
Geändert in Version 3.10: Das Argument loop wurde entfernt.
Veraltet seit Version 3.10: Es wird eine Deprecation-Warnung ausgegeben, wenn keine positionsbezogenen Argumente bereitgestellt werden oder nicht alle positionsbezogenen Argumente zukunftsähnliche Objekte sind und keine laufende Ereignisschleife vorhanden ist.
Eager Task Factory¶
- asyncio.eager_task_factory(loop, coro, *, name=None, context=None)¶
Eine Task-Factory für sofortige Task-Ausführung.
Bei Verwendung dieser Factory (über
loop.set_task_factory(asyncio.eager_task_factory)) beginnen Coroutinen synchron während der Konstruktion vonTask. Tasks werden nur dann in die Ereignisschleife geplant, wenn sie blockieren. Dies kann eine Leistungsverbesserung sein, da der Overhead der Schleifenplanung für Coroutinen vermieden wird, die synchron abgeschlossen werden.Ein gängiges Beispiel, bei dem dies von Vorteil ist, sind Coroutinen, die Caching oder Memoisation verwenden, um tatsächliche I/O-Operationen zu vermeiden, wenn möglich.
Hinweis
Die sofortige Ausführung der Coroutine ist eine semantische Änderung. Wenn die Coroutine zurückkehrt oder eine Ausnahme auslöst, wird der Task nie in die Ereignisschleife geplant. Wenn die Ausführung der Coroutine blockiert, wird der Task in die Ereignisschleife geplant. Diese Änderung kann zu Verhaltensänderungen in bestehenden Anwendungen führen. Zum Beispiel wird die Task-Ausführungsreihenfolge der Anwendung wahrscheinlich geändert.
Hinzugefügt in Version 3.12.
- asyncio.create_eager_task_factory(custom_task_constructor)¶
Erstellt eine „eager“-Task-Factory, ähnlich wie
eager_task_factory(), wobei der bereitgestellte custom_task_constructor bei der Erstellung eines neuen Tasks anstelle des Standard-Taskverwendet wird.custom_task_constructor muss ein Callable sein, dessen Signatur mit der Signatur von
Task.__init__übereinstimmt. Das Callable muss einasyncio.Task-kompatibles Objekt zurückgeben.Diese Funktion gibt ein Callable zurück, das als Task-Factory einer Ereignisschleife über
loop.set_task_factory(factory)verwendet werden kann.Hinzugefügt in Version 3.12.
Vor Abbruch schützen¶
- awaitable asyncio.shield(aw)¶
Schützt ein awaitable Objekt davor, *abgebrochen* zu werden (
cancel()).Wenn aw eine Coroutine ist, wird sie automatisch als Task geplant.
Die Anweisung
task = asyncio.create_task(something()) res = await shield(task)
gleichbedeutend ist mit
res = await something()
außer dass, wenn die enthaltende Koroutine abgebrochen wird, die in
something()laufende Aufgabe nicht abgebrochen wird. Aus der Sicht vonsomething()fand der Abbruch nicht statt. Obwohl ihr Aufrufer immer noch abgebrochen wird, löst der "await"-Ausdruck immer noch einenCancelledErroraus.Wenn
something()aus anderen Gründen abgebrochen wird (d. h. aus sich selbst heraus), würde dies auchshield()abbrechen.Wenn der Abbruch vollständig ignoriert werden soll (nicht empfohlen), sollte die Funktion
shield()mit einer try/except-Klausel kombiniert werden, wie folgt:task = asyncio.create_task(something()) try: res = await shield(task) except CancelledError: res = None
Wichtig
Speichern Sie eine Referenz auf Aufgaben, die dieser Funktion übergeben werden, um zu vermeiden, dass eine Aufgabe mitten in der Ausführung verschwindet. Die Event-Schleife behält nur schwache Referenzen auf Aufgaben. Eine Aufgabe, auf die anderweitig nicht verwiesen wird, kann jederzeit gesammelt werden, auch bevor sie abgeschlossen ist.
Geändert in Version 3.10: Das Argument loop wurde entfernt.
Veraltet seit Version 3.10: Eine Deprecation-Warnung wird ausgegeben, wenn aw kein zukunftsähnliches Objekt ist und keine laufende Event-Schleife vorhanden ist.
Timeouts¶
- asyncio.timeout(delay)¶
Gibt einen asynchronen Kontextmanager zurück, der verwendet werden kann, um die Zeit zu begrenzen, die mit dem Warten auf etwas verbracht wird.
delay kann entweder
Noneoder eine Fließkommazahl/Ganzzahl in Sekunden sein, um zu warten. Wenn delayNoneist, wird keine Zeitbegrenzung angewendet; dies kann nützlich sein, wenn die Verzögerung unbekannt ist, wenn der Kontextmanager erstellt wird.In beiden Fällen kann der Kontextmanager nach der Erstellung mit
Timeout.reschedule()neu geplant werden.Beispiel
async def main(): async with asyncio.timeout(10): await long_running_task()
Wenn die Ausführung von
long_running_tasklänger als 10 Sekunden dauert, wird der Kontextmanager die aktuelle Aufgabe abbrechen und den resultierendenasyncio.CancelledErrorintern behandeln und in einenTimeoutErrorumwandeln, der abgefangen und behandelt werden kann.Hinweis
Der Kontextmanager
asyncio.timeout()ist derjenige, der denasyncio.CancelledErrorin einenTimeoutErrorumwandelt. Das bedeutet, dass derTimeoutErrornur außerhalb des Kontextmanagers abgefangen werden kann.Beispiel für das Abfangen von
TimeoutErrorasync def main(): try: async with asyncio.timeout(10): await long_running_task() except TimeoutError: print("The long operation timed out, but we've handled it.") print("This statement will run regardless.")
Der von
asyncio.timeout()erzeugte Kontextmanager kann auf eine andere Frist neu geplant und inspiziert werden.- class asyncio.Timeout(when)¶
Ein asynchroner Kontextmanager zum Abbrechen überfälliger Koroutinen.
whensollte eine absolute Zeit sein, zu der der Kontext abläuft, gemessen an der Uhr der Event-Schleife.Wenn
whenNoneist, wird der Timeout niemals ausgelöst.Wenn
when < loop.time(), wird der Timeout bei der nächsten Iteration der Event-Schleife ausgelöst.
Beispiel
async def main(): try: # We do not know the timeout when starting, so we pass ``None``. async with asyncio.timeout(None) as cm: # We know the timeout now, so we reschedule it. new_deadline = get_running_loop().time() + 10 cm.reschedule(new_deadline) await long_running_task() except TimeoutError: pass if cm.expired(): print("Looks like we haven't finished on time.")
Timeout-Kontextmanager können sicher verschachtelt werden.
Hinzugefügt in Version 3.11.
- asyncio.timeout_at(when)¶
Ähnlich wie
asyncio.timeout(), mit der Ausnahme, dass when die absolute Zeit ist, zu der mit dem Warten aufgehört werden soll, oderNoneist.Beispiel
async def main(): loop = get_running_loop() deadline = loop.time() + 20 try: async with asyncio.timeout_at(deadline): await long_running_task() except TimeoutError: print("The long operation timed out, but we've handled it.") print("This statement will run regardless.")
Hinzugefügt in Version 3.11.
- async asyncio.wait_for(aw, timeout)¶
Wartet darauf, dass das awaitable-Objekt aw mit einem Timeout abgeschlossen wird.
Wenn aw eine Coroutine ist, wird sie automatisch als Task geplant.
timeout kann entweder
Noneoder eine Fließkommazahl oder Ganzzahl in Sekunden sein, um zu warten. Wenn timeoutNoneist, blockiert, bis die Zukunft abgeschlossen ist.Wenn ein Timeout auftritt, wird die Aufgabe abgebrochen und ein
TimeoutErrorausgelöst.Um den Abbruch der Aufgabe zu vermeiden, wickeln Sie sie in
shield()ein.Die Funktion wartet, bis die Zukunft tatsächlich abgebrochen wurde, sodass die gesamte Wartezeit die timeout-Dauer überschreiten kann. Wenn während des Abbrechens ein Fehler auftritt, wird dieser weitergegeben.
Wenn das Warten abgebrochen wird, wird die Zukunft aw ebenfalls abgebrochen.
Beispiel
async def eternity(): # Sleep for one hour await asyncio.sleep(3600) print('yay!') async def main(): # Wait for at most 1 second try: await asyncio.wait_for(eternity(), timeout=1.0) except TimeoutError: print('timeout!') asyncio.run(main()) # Expected output: # # timeout!
Geändert in Version 3.7: Wenn aw aufgrund eines Timeouts abgebrochen wird, wartet
wait_fordarauf, dass aw abgebrochen wird. Zuvor wurde sofort einTimeoutErrorausgelöst.Geändert in Version 3.10: Das Argument loop wurde entfernt.
Geändert in Version 3.11: Löst stattdessen
TimeoutErrorstattasyncio.TimeoutErroraus.
Warte-Primitive¶
- async asyncio.wait(aws, *, timeout=None, return_when=ALL_COMPLETED)¶
Führt
Future- undTask-Instanzen im Iterable aws gleichzeitig aus und blockiert, bis die Bedingung, die durch return_when angegeben ist, erfüllt ist.Das Iterable aws darf nicht leer sein.
Gibt zwei Mengen von Tasks/Futures zurück:
(done, pending).Verwendung
done, pending = await asyncio.wait(aws)
timeout (eine Fließkommazahl oder Ganzzahl) kann, falls angegeben, verwendet werden, um die maximale Anzahl von Sekunden zu steuern, bevor zurückgekehrt wird.
Beachten Sie, dass diese Funktion keinen
TimeoutErrorauslöst. Futures oder Tasks, die zum Zeitpunkt des Timeouts noch nicht abgeschlossen sind, werden einfach in der zweiten Menge zurückgegeben.return_when gibt an, wann diese Funktion zurückkehren soll. Es muss eine der folgenden Konstanten sein:
Konstante
Beschreibung
- asyncio.FIRST_COMPLETED¶
Die Funktion gibt zurück, wenn ein Future abgeschlossen oder abgebrochen wird.
- asyncio.FIRST_EXCEPTION¶
Die Funktion gibt zurück, wenn ein Future durch Auslösen einer Ausnahme abgeschlossen wird. Wenn kein Future eine Ausnahme auslöst, ist dies äquivalent zu
ALL_COMPLETED.- asyncio.ALL_COMPLETED¶
Die Funktion gibt zurück, wenn alle Futures abgeschlossen oder abgebrochen sind.
Im Gegensatz zu
wait_for()brichtwait()die Futures bei einem Timeout nicht ab.Geändert in Version 3.10: Das Argument loop wurde entfernt.
Geändert in Version 3.11: Das direkte Übergeben von Koroutinen-Objekten an
wait()ist verboten.Geändert in Version 3.12: Unterstützung für Generatoren hinzugefügt, die Tasks liefern.
- asyncio.as_completed(aws, *, timeout=None)¶
Führt awaitable-Objekte im Iterable aws gleichzeitig aus. Das zurückgegebene Objekt kann iteriert werden, um die Ergebnisse der awaitables zu erhalten, sobald sie abgeschlossen sind.
Das von
as_completed()zurückgegebene Objekt kann als asynchroner Iterator oder als einfacher Iterator iteriert werden. Wenn eine asynchrone Iteration verwendet wird, werden die ursprünglich bereitgestellten awaitables zurückgegeben, wenn sie Tasks oder Futures sind. Dies erleichtert die Korrelation von zuvor geplanten Tasks mit ihren Ergebnissen. Beispiel:ipv4_connect = create_task(open_connection("127.0.0.1", 80)) ipv6_connect = create_task(open_connection("::1", 80)) tasks = [ipv4_connect, ipv6_connect] async for earliest_connect in as_completed(tasks): # earliest_connect is done. The result can be obtained by # awaiting it or calling earliest_connect.result() reader, writer = await earliest_connect if earliest_connect is ipv6_connect: print("IPv6 connection established.") else: print("IPv4 connection established.")
Während der asynchronen Iteration werden implizit erstellte Tasks für bereitgestellte awaitables zurückgegeben, die keine Tasks oder Futures sind.
Wenn es als einfacher Iterator verwendet wird, gibt jede Iteration eine neue Koroutine zurück, die das Ergebnis der nächsten abgeschlossenen Awaitable zurückgibt oder die Ausnahme auslöst. Dieses Muster ist mit Python-Versionen vor 3.13 kompatibel.
ipv4_connect = create_task(open_connection("127.0.0.1", 80)) ipv6_connect = create_task(open_connection("::1", 80)) tasks = [ipv4_connect, ipv6_connect] for next_connect in as_completed(tasks): # next_connect is not one of the original task objects. It must be # awaited to obtain the result value or raise the exception of the # awaitable that finishes next. reader, writer = await next_connect
Ein
TimeoutErrorwird ausgelöst, wenn der Timeout auftritt, bevor alle awaitables abgeschlossen sind. Dies wird durch dieasync for-Schleife während der asynchronen Iteration oder durch die während der einfachen Iteration zurückgegebenen Koroutinen ausgelöst.Geändert in Version 3.10: Das Argument loop wurde entfernt.
Veraltet seit Version 3.10: Eine Deprecation-Warnung wird ausgegeben, wenn nicht alle awaitable-Objekte im Iterable aws zukunftsähnliche Objekte sind und keine laufende Event-Schleife vorhanden ist.
Geändert in Version 3.12: Unterstützung für Generatoren hinzugefügt, die Tasks liefern.
Geändert in Version 3.13: Das Ergebnis kann jetzt entweder als asynchroner Iterator oder als einfacher Iterator verwendet werden (zuvor war es nur ein einfacher Iterator).
Ausführung in Threads¶
- async asyncio.to_thread(func, /, *args, **kwargs)¶
Führt die Funktion func asynchron in einem separaten Thread aus.
Alle für diese Funktion bereitgestellten *args und **kwargs werden direkt an func übergeben. Außerdem wird der aktuelle
contextvars.Contextpropagiert, sodass Variablen aus dem Kontext des Event-Loops im separaten Thread zugänglich sind.Gibt eine Koroutine zurück, die awaitet werden kann, um das Endergebnis von func zu erhalten.
Diese Koroutinenfunktion ist hauptsächlich für die Ausführung von E/A-gebundenen Funktionen/Methoden gedacht, die andernfalls die Event-Schleife blockieren würden, wenn sie im Hauptthread ausgeführt würden. Zum Beispiel:
def blocking_io(): print(f"start blocking_io at {time.strftime('%X')}") # Note that time.sleep() can be replaced with any blocking # IO-bound operation, such as file operations. time.sleep(1) print(f"blocking_io complete at {time.strftime('%X')}") async def main(): print(f"started main at {time.strftime('%X')}") await asyncio.gather( asyncio.to_thread(blocking_io), asyncio.sleep(1)) print(f"finished main at {time.strftime('%X')}") asyncio.run(main()) # Expected output: # # started main at 19:50:53 # start blocking_io at 19:50:53 # blocking_io complete at 19:50:54 # finished main at 19:50:54
Direktes Aufrufen von
blocking_io()in einer beliebigen Koroutine würde die Event-Schleife für ihre Dauer blockieren, was zu einer zusätzlichen Laufzeit von 1 Sekunde führt. Stattdessen können wir durch die Verwendung vonasyncio.to_thread()sie in einem separaten Thread ausführen, ohne die Event-Schleife zu blockieren.Hinweis
Aufgrund des GIL kann
asyncio.to_thread()typischerweise nur verwendet werden, um E/A-gebundene Funktionen nicht-blockierend zu machen. Für Erweiterungsmodule, die das GIL freigeben, oder für alternative Python-Implementierungen, die kein GIL haben, kannasyncio.to_thread()auch für CPU-gebundene Funktionen verwendet werden.Hinzugefügt in Version 3.9.
Planung aus anderen Threads¶
- asyncio.run_coroutine_threadsafe(coro, loop)¶
Übergibt eine Koroutine an die angegebene Event-Schleife. Thread-sicher.
Gibt eine
concurrent.futures.Futurezurück, um auf das Ergebnis aus einem anderen Betriebssystem-Thread zu warten.Diese Funktion ist dafür gedacht, von einem anderen Betriebssystem-Thread als dem aufgerufen zu werden, in dem die Event-Schleife läuft. Beispiel:
def in_thread(loop: asyncio.AbstractEventLoop) -> None: # Run some blocking IO pathlib.Path("example.txt").write_text("hello world", encoding="utf8") # Create a coroutine coro = asyncio.sleep(1, result=3) # Submit the coroutine to a given loop future = asyncio.run_coroutine_threadsafe(coro, loop) # Wait for the result with an optional timeout argument assert future.result(timeout=2) == 3 async def amain() -> None: # Get the running loop loop = asyncio.get_running_loop() # Run something in a thread await asyncio.to_thread(in_thread, loop)
Es ist auch möglich, den umgekehrten Weg zu gehen. Beispiel:
@contextlib.contextmanager 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() with concurrent.futures.ThreadPoolExecutor(1) as 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() try: yield loop finally: loop.call_soon_threadsafe(stop_event.set) else: fut.result() # Create a loop in another thread with loop_in_thread() as loop: # Create a coroutine coro = asyncio.sleep(1, result=3) # Submit the coroutine to a given loop future = asyncio.run_coroutine_threadsafe(coro, loop) # Wait for the result with an optional timeout argument assert future.result(timeout=2) == 3
Wenn in der Koroutine eine Ausnahme ausgelöst wird, wird die zurückgegebene Future benachrichtigt. Sie kann auch verwendet werden, um die Aufgabe in der Event-Schleife abzubrechen.
try: result = future.result(timeout) except TimeoutError: print('The coroutine took too long, cancelling the task...') future.cancel() except Exception as exc: print(f'The coroutine raised an exception: {exc!r}') else: print(f'The coroutine returned: {result!r}')
Siehe den Abschnitt Concurrency and Multithreading in der Dokumentation.
Im Gegensatz zu anderen asyncio-Funktionen erfordert diese Funktion, dass das Argument loop explizit übergeben wird.
Hinzugefügt in Version 3.5.1.
Introspektion¶
- asyncio.current_task(loop=None)¶
Gibt die aktuell laufende
Task-Instanz zurück oderNone, wenn keine Aufgabe läuft.Wenn loop
Noneist, wirdget_running_loop()verwendet, um die aktuelle Schleife zu erhalten.Hinzugefügt in Version 3.7.
- asyncio.all_tasks(loop=None)¶
Gibt eine Menge von noch nicht abgeschlossenen
Task-Objekten zurück, die von der Schleife ausgeführt werden.Wenn loop
Noneist, wirdget_running_loop()verwendet, um die aktuelle Schleife zu erhalten.Hinzugefügt in Version 3.7.
- asyncio.iscoroutine(obj)¶
Gibt
Truezurück, wenn obj ein Koroutinenobjekt ist.Hinzugefügt in Version 3.4.
Task-Objekt¶
- class asyncio.Task(coro, *, loop=None, name=None, context=None, eager_start=False)¶
Ein
Future-ähnlichesObjekt, das eine Python-Koroutine ausführt. Nicht thread-sicher.Aufgaben werden verwendet, um Koroutinen in Event-Schleifen auszuführen. Wenn eine Koroutine auf eine Future wartet, pausiert die Aufgabe die Ausführung der Koroutine und wartet auf den Abschluss der Future. Wenn die Future fertig ist, wird die Ausführung der umwickelten Koroutine fortgesetzt.
Event-Schleifen verwenden kooperatives Scheduling: eine Event-Schleife führt jeweils eine Aufgabe aus. Während eine Aufgabe auf den Abschluss einer Future wartet, führt die Event-Schleife andere Aufgaben, Rückrufe aus oder führt E/A-Operationen durch.
Verwenden Sie die High-Level-Funktion
asyncio.create_task()zum Erstellen von Tasks oder die Low-Level-Funktionenloop.create_task()oderensure_future(). Die manuelle Instanziierung von Tasks wird nicht empfohlen.Um eine laufende Aufgabe abzubrechen, verwenden Sie die Methode
cancel(). Das Aufrufen dieser Methode bewirkt, dass die Aufgabe eine Ausnahme vom TypCancelledErrorin die umwickelte Koroutine wirft. Wenn eine Koroutine während des Abbrechens auf ein Future-Objekt wartet, wird das Future-Objekt abgebrochen.cancelled()kann verwendet werden, um zu überprüfen, ob die Aufgabe abgebrochen wurde. Die Methode gibtTruezurück, wenn die umwickelte Koroutine die AusnahmeCancelledErrornicht unterdrückt hat und tatsächlich abgebrochen wurde.asyncio.Taskerbt vonFuturealle seine APIs außerFuture.set_result()undFuture.set_exception().Ein optionales Schlüsselwort-Argument context ermöglicht die Angabe eines benutzerdefinierten
contextvars.Contextfür die Ausführung von coro. Wenn kein context bereitgestellt wird, kopiert die Aufgabe den aktuellen Kontext und führt ihre Koroutine später im kopierten Kontext aus.Ein optionales Schlüsselwort-Argument eager_start ermöglicht das sofortige Starten der Ausführung der
asyncio.Taskzum Zeitpunkt der Task-Erstellung. Wenn es aufTruegesetzt ist und die Event-Schleife läuft, beginnt die Aufgabe sofort mit der Ausführung der Koroutine, bis zum ersten Mal, an dem die Koroutine blockiert. Wenn die Koroutine ohne Blockieren zurückkehrt oder eine Ausnahme auslöst, ist die Aufgabe sofort beendet und überspringt die Planung zur Event-Schleife.Geändert in Version 3.7: Unterstützung für das Modul
contextvarshinzugefügt.Geändert in Version 3.8: Parameter name hinzugefügt.
Veraltet seit Version 3.10: Eine Deprecation-Warnung wird ausgegeben, wenn loop nicht angegeben ist und keine laufende Event-Schleife vorhanden ist.
Geändert in Version 3.11: Parameter context hinzugefügt.
Geändert in Version 3.12: Der Parameter eager_start wurde hinzugefügt.
- done()¶
Gibt
Truezurück, wenn die Aufgabe fertig ist.Eine Aufgabe ist fertig, wenn die umwickelte Koroutine entweder einen Wert zurückgegeben, eine Ausnahme ausgelöst oder die Aufgabe abgebrochen wurde.
- result()¶
Gibt das Ergebnis der Aufgabe zurück.
Wenn die Aufgabe fertig ist, wird das Ergebnis der umwickelten Koroutine zurückgegeben (oder wenn die Koroutine eine Ausnahme ausgelöst hat, wird diese Ausnahme erneut ausgelöst).
Wenn die Aufgabe abgebrochen wurde, löst diese Methode eine Ausnahme vom Typ
CancelledErroraus.Wenn das Ergebnis der Aufgabe noch nicht verfügbar ist, löst diese Methode eine Ausnahme vom Typ
InvalidStateErroraus.
- exception()¶
Gibt die Ausnahme der Aufgabe zurück.
Wenn die umschlossene Koroutine eine Ausnahme ausgelöst hat, wird diese Ausnahme zurückgegeben. Wenn die umschlossene Koroutine normal zurückgegeben hat, gibt diese Methode
Nonezurück.Wenn die Aufgabe abgebrochen wurde, löst diese Methode eine Ausnahme vom Typ
CancelledErroraus.Wenn der Task noch nicht erledigt ist, löst diese Methode eine
InvalidStateErrorAusnahme aus.
- add_done_callback(callback, *, context=None)¶
Fügt einen Callback hinzu, der ausgeführt werden soll, wenn der Task erledigt ist.
Diese Methode sollte nur in Low-Level-Callback-basiertem Code verwendet werden.
Weitere Details finden Sie in der Dokumentation von
Future.add_done_callback().
- remove_done_callback(callback)¶
Entfernt callback aus der Callback-Liste.
Diese Methode sollte nur in Low-Level-Callback-basiertem Code verwendet werden.
Weitere Details finden Sie in der Dokumentation von
Future.remove_done_callback().
- get_stack(*, limit=None)¶
Gibt die Liste der Stack-Frames für diesen Task zurück.
Wenn die umschlossene Koroutine noch nicht erledigt ist, wird der Stack zurückgegeben, an dem sie unterbrochen ist. Wenn die Koroutine erfolgreich abgeschlossen oder abgebrochen wurde, gibt dies eine leere Liste zurück. Wenn die Koroutine durch eine Ausnahme beendet wurde, gibt dies die Liste der Traceback-Frames zurück.
Die Frames sind immer von ältesten zu neuesten sortiert.
Für eine unterbrochene Koroutine wird nur ein Stack-Frame zurückgegeben.
Das optionale Argument limit legt die maximale Anzahl zurückzugebender Frames fest; standardmäßig werden alle verfügbaren Frames zurückgegeben. Die Reihenfolge der zurückgegebenen Liste unterscheidet sich je nachdem, ob ein Stack oder ein Traceback zurückgegeben wird: die neuesten Frames eines Stacks werden zurückgegeben, aber die ältesten Frames eines Tracebacks werden zurückgegeben. (Dies entspricht dem Verhalten des traceback-Moduls.)
- print_stack(*, limit=None, file=None)¶
Gibt den Stack oder Traceback für diesen Task aus.
Dies erzeugt eine Ausgabe, die der des traceback-Moduls für die von
get_stack()abgerufenen Frames ähnelt.Das Argument limit wird direkt an
get_stack()übergeben.Das Argument file ist ein I/O-Stream, in den die Ausgabe geschrieben wird; standardmäßig wird die Ausgabe nach
sys.stdoutgeschrieben.
- get_coro()¶
Gibt das von der
Taskumschlossene Koroutine-Objekt zurück.Hinweis
Dies gibt
Nonefür Tasks zurück, die bereits eifrig abgeschlossen wurden. Siehe Eager Task Factory.Hinzugefügt in Version 3.8.
Geändert in Version 3.12: Die neu hinzugefügte eifrige Task-Ausführung bedeutet, dass das Ergebnis
Nonesein kann.
- get_context()¶
Gibt das mit dem Task assoziierte
contextvars.Context-Objekt zurück.Hinzugefügt in Version 3.12.
- get_name()¶
Gibt den Namen des Tasks zurück.
Wenn dem Task kein Name explizit zugewiesen wurde, generiert die Standardimplementierung von asyncio Task während der Instanziierung einen Standardnamen.
Hinzugefügt in Version 3.8.
- set_name(value)¶
Setzt den Namen des Tasks.
Das Argument value kann jedes Objekt sein, das dann in einen String konvertiert wird.
In der Standardimplementierung von Task ist der Name in der
repr()-Ausgabe eines Task-Objekts sichtbar.Hinzugefügt in Version 3.8.
- cancel(msg=None)¶
Fordert die Abbrechung des Tasks an.
Wenn der Task bereits erledigt oder abgebrochen ist, gibt
Falsezurück, ansonstenTrue.Die Methode veranlasst, dass eine
CancelledErrorAusnahme in der nächsten Schleife der Event-Schleife in die umschlossene Koroutine geworfen wird.Die Koroutine hat dann die Möglichkeit, aufzuräumen oder die Anforderung sogar abzulehnen, indem sie die Ausnahme mit einem
try… …except CancelledError…finallyBlock unterdrückt. Daher garantiertTask.cancel()im Gegensatz zuFuture.cancel()nicht, dass der Task abgebrochen wird, obwohl die Unterdrückung des Abbruchs nicht üblich ist und aktiv davon abgeraten wird. Sollte sich die Koroutine dennoch entscheiden, den Abbruch zu unterdrücken, muss sie zusätzlich zur Behandlung der AusnahmeTask.uncancel()aufrufen.Geändert in Version 3.9: Der Parameter msg wurde hinzugefügt.
Geändert in Version 3.11: Der Parameter
msgwird vom abgebrochenen Task an seinen Waiter weitergegeben.Das folgende Beispiel zeigt, wie Koroutinen die Abbruchsanforderung abfangen können
async def cancel_me(): print('cancel_me(): before sleep') try: # Wait for 1 hour await asyncio.sleep(3600) except asyncio.CancelledError: print('cancel_me(): cancel sleep') raise finally: print('cancel_me(): after sleep') async def main(): # Create a "cancel_me" Task task = asyncio.create_task(cancel_me()) # Wait for 1 second await asyncio.sleep(1) task.cancel() try: await task except asyncio.CancelledError: print("main(): cancel_me is cancelled now") asyncio.run(main()) # Expected output: # # cancel_me(): before sleep # cancel_me(): cancel sleep # cancel_me(): after sleep # main(): cancel_me is cancelled now
- cancelled()¶
Gibt
Truezurück, wenn der Task abgebrochen ist.Der Task ist abgebrochen, wenn der Abbruch mit
cancel()angefordert wurde und die umschlossene Koroutine die hineingeworfeneCancelledErrorAusnahme weitergegeben hat.
- uncancel()¶
Dekrementiert die Anzahl der Abbruchanforderungen an diesen Task.
Gibt die verbleibende Anzahl von Abbruchanforderungen zurück.
Beachten Sie, dass nach Abschluss der Ausführung eines abgebrochenen Tasks weitere Aufrufe von
uncancel()unwirksam sind.Hinzugefügt in Version 3.11.
Diese Methode wird von den internen asyncio-Mechanismen verwendet und ist nicht für die Verwendung durch Endbenutzer gedacht. Insbesondere wenn ein Task erfolgreich wieder aufgerufen wird, ermöglicht dies, dass strukturierte Nebenläufigkeitskonstrukte wie Task Groups und
asyncio.timeout()weiterlaufen und den Abbruch auf den jeweiligen strukturierten Block beschränken. Zum Beispielasync def make_request_with_timeout(): try: async with asyncio.timeout(1): # Structured block affected by the timeout: await make_request() await make_another_request() except TimeoutError: log("There was a timeout") # Outer code not affected by the timeout: await unrelated_code()
Während der Block mit
make_request()undmake_another_request()aufgrund des Timeouts abgebrochen werden könnte, sollteunrelated_code()auch im Falle eines Timeouts weiterlaufen. Dies wird mituncancel()implementiert.TaskGroupKontextmanager verwendenuncancel()auf ähnliche Weise.Wenn Endbenutzercode aus irgendeinem Grund den Abbruch unterdrückt, indem er
CancelledErrorabfängt, muss er diese Methode aufrufen, um den Abbruchszustand zu entfernen.Wenn diese Methode die Abbruchszahl auf Null dekrementiert, prüft die Methode, ob ein vorheriger Aufruf von
cancel()veranlasst hat, dassCancelledErrorin den Task geworfen wird. Wenn dies noch nicht geschehen ist, wird diese Anordnung rückgängig gemacht (durch Zurücksetzen des internen Flags_must_cancel).
Geändert in Version 3.13: Geändert, um ausstehende Abbruchanforderungen beim Erreichen von Null zurückzuziehen.
- cancelling()¶
Gibt die Anzahl der ausstehenden Abbruchanforderungen an diesen Task zurück, d.h. die Anzahl der Aufrufe von
cancel()abzüglich der Anzahl der Aufrufe vonuncancel().Beachten Sie, dass diese Zahl, wenn sie größer als Null ist, der Task aber noch ausgeführt wird,
cancelled()immer nochFalsezurückgibt. Dies liegt daran, dass diese Zahl durch Aufrufe vonuncancel()reduziert werden kann, was dazu führen kann, dass der Task nicht abgebrochen wird, wenn die Abbruchanforderungen auf Null sinken.Diese Methode wird von den internen asyncio-Mechanismen verwendet und ist nicht für die Verwendung durch Endbenutzer gedacht. Siehe
uncancel()für weitere Details.Hinzugefügt in Version 3.11.