So verwenden Sie Asyncio in Python

Mit der asynchronen Programmierfunktion von Python, kurz Async, können Sie Programme schreiben, die mehr Arbeit erledigen, indem Sie nicht auf den Abschluss unabhängiger Aufgaben warten. Die asyncioin Python enthaltene Bibliothek bietet Ihnen die Tools, um Async für die Verarbeitung von Festplatten- oder Netzwerk-E / A zu verwenden, ohne dass alles andere warten muss.

asyncio bietet zwei Arten von APIs für den Umgang mit asynchronen Vorgängen:  High-Level  und  Low-Level . Die High-Level-APIs sind am allgemeinsten nützlich und können auf die unterschiedlichsten Anwendungen angewendet werden. Die Low-Level-APIs sind leistungsstark, aber auch komplex und werden weniger häufig verwendet.

Wir werden uns in diesem Artikel auf die allgemeinen APIs konzentrieren. In den folgenden Abschnitten werden die am häufigsten verwendeten APIs auf hoher Ebene beschrieben  asynciound gezeigt, wie sie für allgemeine Vorgänge mit asynchronen Aufgaben verwendet werden können. 

Wenn Sie mit Async in Python noch nicht vertraut sind oder eine Auffrischung der Funktionsweise benötigen, lesen Sie meine Einführung in Python Async, bevor Sie hier eintauchen.

Führen Sie Coroutinen und Aufgaben in Python aus

Am häufigsten werden natürlich asynciodie asynchronen Teile Ihres Python-Skripts ausgeführt. Dies bedeutet, dass Sie lernen, mit Coroutinen und Aufgaben zu arbeiten. 

Die asynchronen Komponenten von Python, einschließlich Coroutinen und Aufgaben, können nur mit anderen asynchronen Komponenten und nicht mit herkömmlichem synchronem Python verwendet werden. Sie müssen  asyncio daher die Lücke schließen. Dazu verwenden Sie die  asyncio.run Funktion:

Asyncio importieren

async def main ():

Drucken ("5 Sekunden warten.")

für _ im Bereich (5):

warte auf asyncio.sleep (1)

drucken (".")

Drucken ("Warten beendet.")

asyncio.run (main ())

Dies läuft  main()zusammen mit allen Coroutinen  main() ab und wartet auf die Rückkehr eines Ergebnisses.

In der Regel sollte ein Python-Programm nur eine  .run() Anweisung haben, genauso wie ein Python-Programm nur eine  main() Funktion haben sollte. Async kann bei unachtsamer Verwendung den Kontrollfluss eines Programms schwer lesbar machen. Ein einziger Einstiegspunkt in den Async-Code eines Programms verhindert, dass die Dinge haarig werden.

Asynchrone Funktionen können auch als Aufgaben oder Objekte geplant werden, die Coroutinen umschließen  und deren Ausführung unterstützen.

async def my_task ():

etwas tun()

task = asyncio.create_task (my_task ())

my_task() wird dann in der Ereignisschleife ausgeführt, wobei die Ergebnisse in gespeichert werden  task.

Wenn Sie nur eine Aufgabe haben, von der Sie Ergebnisse erhalten möchten, können Sie  asyncio.wait_for(task) warten, bis die Aufgabe abgeschlossen ist, und dann  task.result() das Ergebnis abrufen. Wenn Sie jedoch eine Reihe von Aufgaben für die Ausführung geplant haben und warten möchten, bis  alle abgeschlossen sind, verwenden Sie  diese Option,  asyncio.wait([task1, task2]) um die Ergebnisse zu erfassen. (Beachten Sie, dass Sie eine Zeitüberschreitung für die Vorgänge festlegen können, wenn diese nicht länger als eine bestimmte Zeit ausgeführt werden sollen.)

Verwalten Sie eine asynchrone Ereignisschleife in Python

Eine weitere häufige Verwendung  asyncio ist das Verwalten der asynchronen  Ereignisschleife . Die Ereignisschleife ist ein Objekt, das asynchrone Funktionen und Rückrufe ausführt. Es wird automatisch erstellt, wenn Sie verwenden  asyncio.run(). Im Allgemeinen möchten Sie nur eine asynchrone Ereignisschleife pro Programm verwenden, um die Verwaltung zu gewährleisten.

Wenn Sie fortgeschrittenere Software wie einen Server schreiben, benötigen Sie Zugriff auf die Ereignisschleife auf niedrigerer Ebene. Zu diesem Zweck können Sie die Motorhaube anheben und direkt mit den Interna der Ereignisschleife arbeiten. Aber für einfache Jobs müssen Sie nicht.

Lesen und Schreiben von Daten mit Streams in Python

Die besten Szenarien für Async sind lang laufende Netzwerkvorgänge, bei denen die Anwendung möglicherweise das Warten auf die Rückgabe eines Ergebnisses durch eine andere Ressource blockiert. Bietet zu diesem Zweck  asyncio Streams an, bei denen es sich um Mechanismen auf hoher Ebene für die Durchführung von Netzwerk-E / A handelt. Dies umfasst die Funktion als Server für Netzwerkanforderungen.

asyncio verwendet zwei Klassen  StreamReader und  StreamWriterzum Lesen und Schreiben aus dem Netzwerk auf hoher Ebene. Wenn Sie aus dem Netzwerk lesen möchten  asyncio.open_connection() , öffnen Sie die Verbindung. Diese Funktion gibt ein Tupel von  StreamReader und  StreamWriter Objekten zurück, und Sie würden   jeweils Methoden .read() und  .write()Methoden verwenden, um zu kommunizieren.

Verwenden Sie zum Empfangen von Verbindungen von Remote-Hosts  asyncio.start_server(). Die asyncio.start_server()Funktion verwendet als Argument eine Rückruffunktion  client_connected_cb, die bei jedem Empfang einer Anfrage aufgerufen wird. Diese Rückruffunktion verwendet Instanzen von  StreamReader und StreamWriter als Argumente, sodass Sie die Lese- / Schreiblogik für den Server verarbeiten können. (Hier finden Sie ein Beispiel für einen einfachen HTTP-Server, der die  asyncio-driven-  aiohttp Bibliothek verwendet.)

Aufgaben in Python synchronisieren

Asynchrone Aufgaben werden normalerweise isoliert ausgeführt, aber manchmal möchten Sie, dass sie miteinander kommunizieren. asyncio bietet Warteschlangen und verschiedene andere Mechanismen zum Synchronisieren zwischen Aufgaben:

  • Warteschlangen : Mit  asyncio Warteschlangen können asynchrone Funktionen Python-Objekte anordnen, die von anderen asynchronen Funktionen verwendet werden sollen. So können beispielsweise Workloads basierend auf ihrem Verhalten auf verschiedene Arten von Funktionen verteilt werden.
  • Synchronisationsprimitive : Sperren, Ereignisse, Bedingungen und Semaphore asynciofunktionieren wie herkömmliche Python-Gegenstücke. 

Bei all diesen Methoden ist zu beachten, dass sie  nicht  threadsicher sind. Dies ist kein Problem für asynchrone Aufgaben, die in derselben Ereignisschleife ausgeführt werden. Wenn Sie jedoch versuchen, Informationen mit Aufgaben in einer anderen Ereignisschleife, einem anderen Betriebssystem-Thread oder einem anderen Prozess zu teilen, müssen Sie dazu das  threading Modul und seine Objekte verwenden.

Wenn Sie  Coroutinen über Thread-Grenzen hinweg starten möchten  , verwenden Sie die  asyncio.run_coroutine_threadsafe() Funktion und übergeben Sie die Ereignisschleife, um sie als Parameter zu verwenden.

Unterbrechen Sie eine Coroutine in Python

Eine andere häufige asynciound unterbesprochene Verwendung von  ist das Warten auf eine beliebige Zeitspanne innerhalb einer Coroutine. Sie können dies nicht verwenden  time.sleep() , oder Sie blockieren das gesamte Programm. Verwenden Sie stattdessen, damit  asyncio.sleep()andere Coroutinen weiter ausgeführt werden können.

Verwenden Sie in Python Async auf niedrigerer Ebene

Wenn Sie der Meinung sind, dass für die von Ihnen erstellte App möglicherweise asynciountergeordnete Komponenten erforderlich sind , schauen Sie sich um, bevor Sie mit dem Codieren beginnen: Es besteht eine gute Chance, dass bereits jemand eine asynchrone Python-Bibliothek erstellt hat, die genau das tut, was Sie benötigen.

Wenn Sie beispielsweise eine asynchrone DNS-Abfrage benötigen, überprüfen Sie die  aiodns Bibliothek, und für asynchrone SSH-Sitzungen gibt es eine  asyncSSH. Durchsuchen Sie PyPI nach dem Schlüsselwort "async" (plus anderen aufgabenbezogenen Schlüsselwörtern) oder suchen Sie in der von Hand kuratierten Awesome Asyncio-Liste nach Ideen.