Depuis Python 3.5, async / await est disponible pour la programmation asynchrone. Cependant, de nombreuses bibliothèques existantes ont une API asynchrone de type callback traditionnelle et async / await ne peut pas être appliqué tel quel. Par conséquent, nous allons convertir une telle API de type de rappel dans un formulaire qui peut être utilisé avec async / await.
Ajoute les arguments a et b sur un autre thread et renvoie le résultat dans un rappel Considérez la méthode async_add ci-dessous.
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()
Utilisons ceci pour faire un calcul 1 + 2 + 3. Ce sera comme suit.
async_add(1, 2, lambda result1: \
async_add(result1, 3, lambda result2: \
print(result2)))
C'est compliqué car les rappels sont imbriqués. Je voudrais conclure ceci afin que je puisse utiliser await pour écrire une image comme celle-ci:
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()
Résultat d'exécution
6
(1) Lorsque la méthode awaitable_async_add est appelée, l'exécution de la méthode async_add est lancée et l'objet asyncio.Future est renvoyé immédiatement. À ce stade, le processus de calcul n'est pas encore terminé.
(2) Le rappel est appelé lorsque le processus de calcul est terminé. En appelant la méthode set_result () de l'objet asyncio.Future avec le résultat du traitement comme argument, l'objet asyncio.Future est notifié de la fin du traitement et le résultat du calcul est renvoyé.
(3) Acquérir une boucle d'événements et exécuter le processus jusqu'à ce que le processus asynchrone soit terminé.
Une chose à noter ici est loop.call_soon_threadsafe dans (2). Si vous essayez d'appeler f.set_result directement comme indiqué ci-dessous, une erreur se produit.
def callback(result):
f.set_result(result)
Résultat d'exécution
RuntimeError: Non-thread-safe operation invoked on an event loop other than the current one
Cette erreur est due au fait que la méthode set_result est appelée sur un thread différent de la boucle d'événements exécutée dans (3). Comme asyncio.Future n'est pas thread-safe, la méthode set_result doit toujours être appelée dans le même thread que la boucle d'événements. Par conséquent, en passant le traitement en tant que rappel à la méthode call_soon_threadsafe de la boucle d'événements, set_result est appelé dans la boucle d'événements.
https://docs.python.org/3.6/library/asyncio.html
Recommended Posts