asyncio: Nebenläufigkeit in Python ohne Threads
Kurz: Mit asyncio laufen viele wartende Aufgaben (Netzwerk, Dateien) in einem einzigen Thread nebeneinander, indem sie sich beim Warten gegenseitig Platz machen.
Genauer: Viele Programme warten die meiste Zeit: auf eine Antwort vom Server, auf die Festplatte, auf die Datenbank. Statt für jede Anfrage einen Thread zu starten, nutzt asyncio kooperatives Multitasking: Ein Event Loop führt Coroutinen aus, und jede gibt beim Warten (await) die Kontrolle ab, sodass eine andere weiterlaufen kann.
Im Detail
Grundbegriffe
- Coroutine: eine Funktion mit
async def, die pausieren kann. - await: pausiert die Coroutine, bis das Ergebnis bereit ist, und lässt den Event Loop andere Arbeit erledigen.
- Task: eine eingeplante Coroutine, die nebenläufig läuft (
asyncio.create_task). - Event Loop: Schleife, die Aufgaben verwaltet und fortsetzt (
asyncio.run).
Beispiel
import asyncio
async def hole(name, sekunden):
await asyncio.sleep(sekunden) # simuliert Warten auf das Netz
return f"{name} fertig"
async def main():
ergebnisse = await asyncio.gather(
hole("A", 2), hole("B", 1), hole("C", 3)
)
print(ergebnisse) # nach ca. 3 s, nicht nach 6 s
asyncio.run(main())Die drei Aufgaben warten gleichzeitig. Die Gesamtzeit entspricht der längsten, nicht der Summe.
Wann es hilft, wann nicht
Hilfreich bei I/O-gebundenen Aufgaben: viele HTTP-Anfragen, Datenbankzugriffe, Websockets, Webserver (FastAPI, aiohttp). Bei rechenintensiven Aufgaben bringt es nichts, da nur ein Thread rechnet; dafür nutzt man multiprocessing oder concurrent.futures.ProcessPoolExecutor. Blockierende Aufrufe wie time.sleep() oder requests.get() innerhalb einer Coroutine blockieren den ganzen Event Loop; verwende asyncio.sleep und asynchrone Bibliotheken (httpx, aiohttp) oder asyncio.to_thread.
Fallen
Vergessenes await erzeugt nur ein Coroutine-Objekt, aber keine Ausführung. Fehler in nicht abgewarteten Tasks gehen leicht unter, nutze TaskGroup (Python 3.11+) für sauberes Fehlerhandling. Beende lange Tasks mit Zeitlimits (asyncio.timeout). Das gleiche Prinzip (async/await) kennst du aus JavaScript, C# und Rust.
Siehe auch: async/await, Event Loop, Nebenläufigkeit