CodeGym /Kurse /Python SELF DE /Asynchrone Methoden

Asynchrone Methoden

Python SELF DE
Level 25 , Lektion 2
Verfügbar

3.1 Modul asyncio

Eigene Threads für asynchrone Unteraufgaben erstellt heutzutage niemand mehr. Genauer gesagt, kann man sie erstellen, aber solche Aktionen gelten als zu niedrigstufig und werden nur von Framework-Entwicklern verwendet. Und auch dann, wenn es wirklich nicht anders geht.

Heute sind asynchrones Programmieren, die Operatoren async/await und Coroutinen mit Tasks angesagt. Aber der Reihe nach…

Ein bisschen Geschichte

Ursprünglich wurden in Python zur Lösung von Aufgaben des asynchronen Programmierens Coroutinen verwendet, die auf Generatoren basieren. Dann, in Python 3.4, wurde das Modul asyncio eingeführt (manchmal wird es auch als async IO bezeichnet), das Mechanismen für asynchrones Programmieren bietet. In Python 3.5 wurde die Konstruktion async/await eingeführt.

Und jetzt ein bisschen Vorgeschichte. Zuerst erkläre ich kurz all diese Dinge, dann ausführlicher und dann noch ausführlicher. Anders geht es nicht, da sie fast alle zusammenarbeiten und es detailliert zu erklären, ohne auf andere zu verweisen, nicht möglich ist.

Modul asyncio

Das Modul asyncio ist für das Schreiben von asynchronen Programmen gedacht und bietet die Möglichkeit, Aufgaben parallel auszuführen. Es unterstützt asynchrone Ein-/Ausgabe-Operationen, Timer, Sockets, die Ausführung von Coroutinen und Multithreading und arbeitet in einem oder mehreren Ereignisschleifen.

Coroutinen (Coroutines)

Coroutinen sind asynchrone Funktionen, die mit dem Schlüsselwort async def definiert werden. Coroutinen erlauben es, ihre Ausführung mit dem Schlüsselwort await zu pausieren, was anderen Coroutinen erlaubt, während dieser Zeit ausgeführt zu werden.


import asyncio

# Definition einer asynchronen Funktion (Coroutine)
async def main():
    print('Hello ...')
    # Ausführung für 1 Sekunde pausieren
    await asyncio.sleep(1)
    print('... World!')

# Ausführung der asynchronen Funktion main() in der Ereignisschleife
asyncio.run(main())

Ereignisschleife (Event Loop)

Die Ereignisschleife verwaltet die Ausführung von Coroutinen, Aufgaben und anderen asynchronen Operationen. Der Aufruf von asyncio.run() startet die Ereignisschleife und führt die Coroutine bis zum Abschluss aus.


import asyncio

async def main():
    print('Hello ...')
    await asyncio.sleep(1)
    print('... World!')

# Aktuelle Ereignisschleife bekommen
loop = asyncio.get_event_loop()
# Coroutine bis zum Ende ausführen
loop.run_until_complete(main())
# Ereignisschleife nach Abschluss aller Aufgaben schließen
loop.close()

Aufgaben (Tasks)

Aufgaben erlauben es, Coroutinen parallel auszuführen. Sie werden mit Hilfe von asyncio.create_task() oder asyncio.ensure_future() erstellt.


import asyncio

# Definition einer Coroutine, die mit Verzögerung ausgeführt wird
async def say_after(delay, what):
    # Ausführung für die angegebene Zeit pausieren
    await asyncio.sleep(delay)
    print(what)

# Hauptcoroutine
async def main():
    # Aufgaben erstellen, um Coroutinen parallel auszuführen
    task1 = asyncio.create_task(say_after(1, 'hello'))
    task2 = asyncio.create_task(say_after(2, 'world'))
    
    # Warten, bis beide Aufgaben abgeschlossen sind
    await task1
    await task2

# Hauptcoroutine starten
asyncio.run(main())

Futures (Futures)

Future-Objekte repräsentieren die Ergebnisse von asynchronen Operationen, die in der Zukunft verfügbar sein werden. Zum Beispiel werden sie verwendet, um auf den Abschluss einer asynchronen Aufgabe zu warten.


import asyncio

# Definition einer Coroutine, die eine langwierige Aufgabe simuliert
async def long_running_task():
    print('Task started')
    # Ausführung für 3 Sekunden pausieren
    await asyncio.sleep(3)
    print('Task finished')
    return 'Result'

# Hauptcoroutine
async def main():
    # Future erstellen, um auf den Abschluss der Aufgabe zu warten
    future = asyncio.ensure_future(long_running_task())
    # Warten, bis die Aufgabe abgeschlossen ist und Ergebnis bekommen
    result = await future  
    print(f'Task result: {result}')

# Hauptcoroutine starten
asyncio.run(main())

3.2 Asynchrone Funktion — async def

Eine asynchrone Funktion wird genauso deklariert wie eine normale, nur dass vor dem Schlüsselwort def das Wort async stehen muss.


async def FunktionsName(parameter):
    funktionscode

Eine asynchrone Funktion wird wie eine normale deklariert, wie eine normale aufgerufen, aber das Ergebnis, das sie zurückgibt, ist anders. Wenn man eine asynchrone Funktion aufruft, gibt sie nicht das Ergebnis zurück, sondern ein spezielles Objekt — eine Coroutine.

Man kann das sogar überprüfen:


import asyncio

async def main():
    print("Hello World")
            
# Aufruf einer asynchronen Funktion, die eine Coroutine zurückgibt
result = main()
# Überprüfen des Typs des Ergebnisses
print(type(result)) # <class 'coroutine'>

Was passiert da? Wenn du eine Funktion mit dem Wort async markierst, fügst du ihr im Wesentlichen einen Dekorator hinzu, der ungefähr Folgendes macht:


def async_decorator(func):
    # Task-Objekt erstellen
    task = Task()
    # Übergabe unserer Funktion func an das Task-Objekt, damit es sie ausführt
    task.target = func  
    # Task-Objekt zur Aufgabenwarteschlange — Event Loop — hinzufügen
    eventloop.add_task(task)  
    # Task-Objekt zurückgeben
    return task 

Und dein Code wird so ähnlich:


import asyncio

@async_decorator
def main():
    print("Hello World")
            
result = main()
print(type(result)) # <class 'coroutine'>

Der Sinn dieser Analogie ist folgender:

Wenn du eine asynchrone Funktion aufrufst, wird ein spezielles Task-Objekt erstellt, das deine Funktion ausführen wird, aber irgendwann in der Zukunft. Vielleicht nach 0.0001 Sekunde oder vielleicht nach 10 Sekunden.

Dieses Objekt task wird dir sofort als Ergebnis des Aufrufs deiner asynchronen Funktion zurückgegeben. Das ist die Coroutine. Deine asynchrone Funktion hat vielleicht noch gar nicht angefangen zu laufen, aber das task (Coroutine)-Objekt hast du schon.

Wofür ist dir dieses task (Coroutine)? Du kannst mit ihm nicht viel anfangen, aber du kannst 3 Dinge tun:

  • Warten, bis die asynchrone Funktion ausgeführt wird.
  • Warten, bis die asynchrone Funktion fertig ist und das Ergebnis der Funktion aus der Coroutine erhalten.
  • Warten, bis 10 (beliebige Anzahl) asynchrone Funktionen ausgeführt sind.

Wie du das machst, erkläre ich weiter unten.

3.3 Operator await

Ein Großteil der Aktionen mit einer Coroutine beginnt mit „warten auf die Ausführung der asynchronen Funktion“. Deshalb gibt es dafür einen speziellen Operator await.

Du musst ihn einfach vor die Coroutine schreiben:


await coroutine

Oder direkt vor den Aufruf der asynchronen Funktion:


await asynchrone_funktion(argumente)

Wenn Python den Operator await im Code trifft, pausiert es die Ausführung der aktuellen Funktion und wartet, bis die Coroutine abgeschlossen ist — bis die asynchrone Funktion, auf die sich die Coroutine bezieht, beendet ist.

Wichtig! Der Operator await wird nur innerhalb einer asynchronen Funktion verwendet, um die Ausführung zu pausieren, bis eine andere Coroutine oder asynchrone Operation abgeschlossen ist.

Das vereinfacht den Prozess des Umschaltens zwischen Aufrufen von asynchronen Funktionen. Ein solcher await-Aufruf ist eigentlich eine Deklaration „wir werden hier für unbestimmte Zeit warten — kümmert euch um die Ausführung anderer asynchroner Funktionen“.

Beispiel:


import asyncio

# Definition einer asynchronen Funktion
async def async_print(text):
    print(text)
        
# Haupt asynchrone Funktion
async def main():
    # Verwendung von await, um auf die Ausführung der asynchronen Funktion zu warten
    await async_print("Hello World")
        
# Starten der Haupt-Ereignisschleife und Ausführung der Coroutine main()
asyncio.run(main()) #startet die asynchrone Funktion

Tatsächlich arbeitet der Operator await noch schlauer — er gibt auch das Ergebnis der asynchronen Funktion zurück, bei der er aufgerufen wurde.

Beispiel:


import asyncio

# Definition einer asynchronen Funktion, die zwei Zahlen addiert
async def async_add(a, b):
    return a + b
        
# Haupt asynchrone Funktion
async def main():
    # Verwendung von await, um das Ergebnis der Ausführung von async_add zu erhalten
    sum = await async_add(100, 200)
    print(sum)
        
# Starten der Haupt-Ereignisschleife und Ausführung der Coroutine main()
asyncio.run(main()) #startet die asynchrone Funktion

Fassen wir zusammen, der Operator await:

  • Pausiert die aktuelle asynchrone Funktion, bis eine andere Coroutine oder asynchrone Operation abgeschlossen ist.
  • Gibt das Ergebnis der asynchronen Operation oder Coroutine zurück.
  • Kann nur innerhalb einer asynchronen Funktion verwendet werden.
Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION