Python async/await Erklärt: Das 2026-Tutorial
Async in Python hat den Ruf, verwirrend zu sein. Das muss nicht sein. Sobald Sie einen Satz verinnerlicht haben – async lässt EINEN Thread mehrere I/O-gebundene Aufgaben verschachtelt ausführen – folgt alles andere.
In diesem Tutorial geht es darum, was „async def“ und „await“ tatsächlich bewirken, wann Async gewinnt und wenn es das falsche Tool ist, die vier Muster, die jede async-Codebasis verwendet (sequentiell, Gather, Semaphore, Timeout) und die drei Arten, wie Anfänger Fehler stillschweigend schlucken.
Das mentale Ein-Satz-Modell
Async in Python ist kooperative Parallelität in einem einzelnen Thread. Wenn Ihr Code auf „Warten“ klickt, kehrt die Steuerung zur Ereignisschleife zurück, die eine andere Aufgabe ausführen kann, während die erste auf E/A wartet. Wenn die E/A abgeschlossen ist, setzt die Schleife die erste Aufgabe an der Stelle fort, an der sie angehalten wurde.
Das ist es. Keine Threads, keine Prozesse, keine Parallelität (im CPython-mit-GIL-Sinn). Nur ein Thread, der viele Aufgaben jongliert, die die meiste Zeit damit verbringen, im Netzwerk, auf der Festplatte oder in der Datenbank zu warten.
Async ist schneller, wenn Ihr Engpass im Warten und nicht im Rechnen liegt.
Das dreizeilige, minimal lebensfähige asynchrone Programm
- „async def hello()“ – deklariert eine Coroutine. Durch den Aufruf von „hello()“ wird der Code nicht ausgeführt; Es erstellt ein Coroutine-Objekt.
- „await asyncio.sleep(1)“ – gibt die Kontrolle für 1 Sekunde an die Ereignisschleife zurück.
- „asyncio.run(hello())“ – erstellt eine Ereignisschleife und führt die Coroutine bis zum Abschluss aus.
Regel: `await` nur innerhalb von `async def`. Der Aufruf von „await“ in einer regulären Funktion ist ein „SyntaxError“.
Wenn Async gewinnt (und wenn nicht)
Async gewinnt: I/O-gebundene Arbeit
Jede Aufgabe, die die meiste Zeit mit WARTEN verbringt, ist ein Kandidat für Asynchronität:
- HTTP-Anfragen (externe APIs, Scraping, Webhooks)
- Datenbankabfragen
- Datei liest/schreibt
- WebSocket-/gRPC-Streams
- Warteschlangenkonsumenten (Redis, RabbitMQ, Kafka)
Beispiel: 100 URLs abrufen.
50- bis 100-fache Beschleunigung für I/O-intensive Arbeiten. Hier verdient Async sein Komplexitätsbudget.
Async VERLIERT: CPU-gebundene Arbeit
Async bietet Ihnen keine Parallelität für die Berechnung. Da es sich um kooperatives Single-Threading handelt, blockiert eine CPU-Hot-Loop die gesamte Ereignisschleife.
Alle anderen Aufgaben bleiben stehen, während „pbkdf2“ ausgeführt wird. Greifen Sie für CPU-gebundene Arbeit nach „Multiprocessing“ oder „concurrent.futures.ProcessPoolExecutor“, nicht nach „Async“.
Gemischte Arbeitslast: „run_in_executor“.
Wenn EIN Schritt CPU-intensiv ist und der Rest E/A-Vorgänge sind, wickeln Sie den CPU-Schritt so ein, dass er in einem Thread-Pool ausgeführt wird, ohne die Schleife zu blockieren:
Der „None“ = Standard-Thread-Pool. Ideal zum Aufrufen von Synchronisierungsbibliotheken von Drittanbietern, die langsam sind, aber nicht neu geschrieben werden können.
Die vier Muster
1. Sequentiell – jeweils eine Aufgabe
Verwenden Sie diese Option, wenn jede Anfrage vom Ergebnis der vorherigen abhängt.
2. „asyncio.gather“ – alles auf einmal abfeuern
Standardauswahl für unabhängige E/A-Aufgaben. Aber: Wenn Sie 10.000 Aufgaben für eine API sammeln, erhalten Sie ratenbegrenzte oder erschöpfte Dateideskriptoren. Hier kommt das Semaphormuster ins Spiel.
3. Semaphor – Parallelität begrenzen
„asyncio.Semaphore(N)“ lässt zu jedem Zeitpunkt höchstens N Aufgaben über „async with sem:“ hinaus. Die anderen Aufgaben stehen in der Warteschlange. Verwendung für jede externe API mit einer Ratenbegrenzung oder eine Datenbank mit einem Verbindungspool.
4. Timeout – geben Sie langsame Arbeit auf
„asyncio.wait_for“ bricht die zugrunde liegende Aufgabe nach N Sekunden ab. Unverzichtbar für jeden asynchronen Code, der mit einem Netzwerk kommuniziert – machen Sie niemals eine unbegrenzte Wartezeit.
Die drei fehlerverschluckenden Fallen
Falle 1: „asyncio.gather“ mit „return_exclusions=True“.
Hilfreich, wenn Sie Teilergebnisse behalten möchten, aber TÖDLICH, wenn Sie nicht jedes Ergebnis überprüfen:
Ohne die Prüfung verschwinden die Fehler stillschweigend.
Falle 2: Fire-and-forget-Aufgaben
Wenn „background_work()“ ausgelöst wird, gibt Python beim Herunterfahren eine Warnung aus und fährt fort. Für alles, was Sie interessiert, speichern Sie die Aufgabe und „warten“ Sie darauf oder verwenden Sie „asyncio.TaskGroup“ (3.11+), das alle Fehler weitergibt.
Falle 3: Code innerhalb von Async synchronisieren
„Anfragen“ ist synchron. Selbst innerhalb einer „async def“ blockiert „requests.get“ die gesamte Schleife, bis die Antwort eintrifft. Verwenden Sie „httpx“ (oder „aiohttp“) für asynchrones HTTP.
Regel: Verwenden Sie niemals eine synchrone E/A-Bibliothek innerhalb von asynchronem Code. Wenn die Bibliothek keine asynchrone Variante hat, schließen Sie sie in „run_in_executor“ ein.
„asyncio.TaskGroup“ – die Art und Weise, sich im Jahr 2026 aufzufächern
Python 3.11 hat „TaskGroup“ hinzugefügt, was das Fire-and-Forget-Problem sauber behebt:
Strukturierte Parallelität – keine verlorenen Fehler, keine verwaisten Aufgaben. Bevorzugen Sie dies gegenüber bloßem „create_task“ in modernem Code.
Beispiel aus der Praxis: geschwindigkeitsbegrenzter Scraper
Alle vier Muster in 15 Zeilen: begrenzte Parallelität (Semaphor), Zeitüberschreitungen („wait_for“), explizite Fehlerverzweigungen (kein Verschlucken) und Sammeln für Fan-Out. Kopieren Sie diese Form und fügen Sie sie in eine beliebige asynchrone E/A-Aufgabe ein.
Async in Python hat eine echte Lernkurve, aber das mentale Modell ist einfach: ein Thread, kooperative Pausen bei „Warten“, I/O-Überlappungen. Beherrschen Sie „gather“ + „Semaphore“ + „wait_for“ + „TaskGroup“ und Sie haben 95 % der asynchronen Arbeit abgedeckt, die Sie jemals schreiben werden.
Nächster Schritt: Der Senior Python Track bietet 20 praktische Lektionen zu Asyncio, TaskGroup, aiohttp/httpx und den oben genannten Parallelitätsmustern – mit echtem Python, das in Ihrem Browser ausgeführt wird.