ProgrammierungBackend Entwickler

Erklären Sie, wie in Python die asynchrone Programmierung mit async/await implementiert ist. Was sind die Vorteile und Herausforderungen dieses Ansatzes?

Bestehen Sie Vorstellungsgespräche mit dem Hintsage-KI-Assistenten

Antwort.

Die asynchrone Programmierung wurde in Python mit Version 3.5 durch die Einführung der Schlüsselwörter async und await eingeführt. Zuvor wurden für asynchrone Aufgaben Bibliotheken wie asyncio und Generatoren auf Basis von Koroutinen verwendet, was oft schwierig zu verstehen und zu warten war. Mit dem neuen async/await-Syntax wurde der asynchrone Code jedoch klarer und lesbarer und ähnelte dem gewohnten synchronen Stil.

Historische Aspekte

Vor der Einführung von async/await wurde die Asynchronität durch Rückrufe und Generatoren (z. B. mit der Bibliothek tornado oder alten asyncio-APIs) implementiert. Solcher Code war schwer zu debuggen und zu warten.

Probleme

Das Hauptproblem bei der Verarbeitung einer großen Anzahl gleichzeitiger I/O-Operationen (Netzwerkanfragen, Datei-Ein/Ausgaben) in synchronem Code ist die Blockierung des Hauptthreads. Dies führt zu einer verminderten Leistung und einer ineffizienten Ressourcennutzung.

Lösung

Die asynchrone Programmierung mit async/await ermöglicht es, viele Ein- und Ausgabeoperationen gleichzeitig in einem einzigen Thread auszuführen, ohne Blockierungen zu verursachen. Der Syntax ist dabei nah an gewöhnlichen Funktionen, was die Lesbarkeit und das Debuggen erleichtert.

Beispielcode:

import asyncio async def fetch_data(delay): print(f"Start fetching after {delay}s delay") await asyncio.sleep(delay) print(f"Done fetching after {delay}s delay") return delay async def main(): results = await asyncio.gather( fetch_data(1), fetch_data(2), fetch_data(3) ) print("Results:", results) asyncio.run(main())

Schlüsselmerkmale:

  • Keine Blockierung des Threads: Ein- und Ausgabeoperationen geben den Thread frei.
  • Eindeutiger Syntax (async/await) für asynchrone Aufrufe.
  • Einfache Integration mit Bibliotheken, die asyncio unterstützen.

Trickfragen.

Kann eine Funktion, die mit async def definiert ist, wie eine normale Funktion aufgerufen werden?

Nein. Der Aufruf einer solchen Funktion gibt ein Koroutinenobjekt zurück, das nicht ausgeführt wird, bis es in die Ereignisschleife übergeben wird (z. B. mit await oder asyncio.run()).

def foo(): return 42 async def bar(): return 42 print(foo()) # 42 print(bar()) # <coroutine object bar at ...>

Kann await außerhalb einer asynchronen Funktion verwendet werden?

Nein. Das Schlüsselwort await darf nur innerhalb von Funktionen verwendet werden, die mit async def deklariert sind. Ein Versuch, await außerhalb einer solchen Funktion zu verwenden, führt zu einem Syntaxfehler.

# Fehler! await asyncio.sleep(1) # SyntaxError: 'await' outside async function

Funktioniert Asynchronität für Operationen, die nicht mit I/O zu tun haben (z. B. Berechnungen)?

Nein. Asynchronität ist nur für Ein- und Ausgabeoperationen effektiv. Für rechenintensive Aufgaben sind nach wie vor multiprocessing oder threading erforderlich, andernfalls wird die Ereignisschleife blockiert.

Typische Fehler und Anti-Patterns

Vorteile:

  • Reduziert die Wartezeit für I/O-Operationen erheblich.
  • Erhöht die Reaktionsfähigkeit von Servern und Anwendungen.
  • Beibehaltung des monolithischen Charakters des Codes, wodurch Probleme der Mehrprozessverarbeitung umgangen werden.

Nachteile:

  • Asynchrone Codes sind schwer zu testen.
  • Bibliotheken müssen asynchrone Unterstützung bieten (nicht alle unterstützen asyncio).
  • Es ist ein Missverständnis, dass Asynchronität die Berechnungsgeschwindigkeit erhöht — das gilt nur für I/O.

Beispiel aus dem Leben

Negativer Fall: Junge Entwickler versuchten, eine Anwendung mit async/await zu beschleunigen, machten jedoch asynchron nur Berechnungen und keine Netzwerkanfragen. Die Anwendung wurde nicht schneller. Vorteile: Sie machten sich mit der Syntax vertraut. Nachteile: kein Gewinn, der Code wurde komplizierter.

Positiver Fall: Asynchron wurden Tausende von Anfragen an die API bearbeitet. Der Server konnte mehr Kunden bedienen, ohne die Ressourcen zu erhöhen. Vorteile: Die Leistung stieg erheblich, die Architektur wurde einfacher. Nachteile: Die Einstiegshürde für Neulinge stieg.