Seit Python 3.5 ist async / await für die asynchrone Programmierung verfügbar. Viele vorhandene Bibliotheken verfügen jedoch über eine herkömmliche asynchrone API vom Rückruftyp, und async / await kann nicht so angewendet werden, wie es ist. Daher werden wir eine solche Rückruf-API in ein Formular konvertieren, das mit async / await verwendet werden kann.
Fügt die Argumente a und b zu einem anderen Thread hinzu und gibt das Ergebnis in einem Rückruf zurück Betrachten Sie die folgende async_add-Methode.
import time
import threading
def async_add(a, b, callback):
def run():
time.sleep(1)
callback(a + b)
thread = threading.Thread(target=run)
thread.start()
Verwenden wir dies, um eine 1 + 2 + 3-Berechnung durchzuführen. Es wird wie folgt sein.
async_add(1, 2, lambda result1: \
async_add(result1, 3, lambda result2: \
print(result2)))
Es ist kompliziert, weil die Rückrufe verschachtelt sind. Ich möchte dies zusammenfassen, damit ich mit wait ein Bild wie das folgende schreiben kann:
result1 = await awaitable_async_add(1, 2)
result2 = await awaitable_async_add(result1, 3)
print(result2)
Code
import time
import threading
import asyncio
def async_add(a, b, callback):
def run():
time.sleep(1)
callback(a + b)
thread = threading.Thread(target=run)
thread.start()
def awaitable_async_add(a, b, loop):
f = asyncio.Future() # (1)
def callback(result):
loop.call_soon_threadsafe(
lambda: f.set_result(result)) #(2)
async_add(a, b, callback) # (1)
return f # (1)
async def exec(loop):
result1 = await awaitable_async_add(1, 2, loop)
result2 = await awaitable_async_add(result1, 3, loop)
print(result2)
loop = asyncio.get_event_loop() # (3)
loop.run_until_complete(exec(loop)) # (3)
loop.stop()
Ausführungsergebnis
6
(1) Wenn die Methode awaitable_async_add aufgerufen wird, wird die Ausführung der Methode async_add gestartet und das Objekt asyncio.Future wird sofort zurückgegeben. Zu diesem Zeitpunkt ist der Berechnungsprozess noch nicht abgeschlossen.
(2) Der Rückruf wird aufgerufen, wenn der Berechnungsprozess abgeschlossen ist. Durch Aufrufen der set_result () -Methode des asyncio.Future-Objekts mit dem Verarbeitungsergebnis als Argument wird das asyncio.Future-Objekt über den Abschluss der Verarbeitung benachrichtigt und das Berechnungsergebnis zurückgegeben.
(3) Erfassen Sie eine Ereignisschleife und führen Sie den Prozess aus, bis der asynchrone Prozess abgeschlossen ist.
Eine Sache, die hier zu beachten ist, ist loop.call_soon_threadsafe in (2). Wenn Sie versuchen, f.set_result wie unten gezeigt direkt aufzurufen, tritt ein Fehler auf.
def callback(result):
f.set_result(result)
Ausführungsergebnis
RuntimeError: Non-thread-safe operation invoked on an event loop other than the current one
Dieser Fehler ist darauf zurückzuführen, dass die Methode set_result in einem anderen Thread als der in (3) ausgeführten Ereignisschleife aufgerufen wird. Da asyncio.Future nicht threadsicher ist, muss die Methode set_result immer im selben Thread wie die Ereignisschleife aufgerufen werden. Daher wird set_result in der Ereignisschleife aufgerufen, indem die Verarbeitung als Rückruf an die Methode call_soon_threadsafe der Ereignisschleife übergeben wird.
https://docs.python.org/3.6/library/asyncio.html
Recommended Posts