J'utilise aiohttp pour construire un serveur API qui envoie des requêtes TCP à un serveur séparé. Le module qui envoie les TCP = les demandes sont synchrones et une boîte noire à mes fins. Mon problème est donc que ces demandes bloquent l'intégralité de l'API. J'ai besoin d'un moyen d'envelopper les demandes de module dans une coroutine asynchrone qui ne bloquera pas le reste de l'API.
Donc, en utilisant simplement sleep
comme exemple simple, existe-t-il un moyen d'envelopper en quelque sorte du code synchrone chronophage dans une coroutine non bloquante, quelque chose comme ceci:
async def sleep_async(delay):
# After calling sleep, loop should be released until sleep is done
yield sleep(delay)
return 'I slept asynchronously'
Finalement, j'ai trouvé une réponse dans ce fil . La méthode que je cherchais est run_in_executor . Cela permet à une fonction synchrone d'être exécutée de manière asynchrone sans bloquer une boucle d'événements.
Dans l'exemple sleep
que j'ai publié ci-dessus, cela pourrait ressembler à ceci:
import asyncio
from time import sleep
from concurrent.futures import ProcessPoolExecutor
async def sleep_async(loop, delay):
# Can set executor to None if a default has been set for loop
await loop.run_in_executor(ProcessPoolExecutor(), sleep, delay)
return 'I slept asynchronously'
Voir aussi la réponse suivante -> Comment appelons-nous une fonction normale où une coroutine est attendue?
Vous pouvez utiliser un décorateur pour envelopper la version de synchronisation dans une version asynchrone.
import time
from functools import wraps, partial
def wrap(func):
@wraps(func)
async def run(*args, loop=None, executor=None, **kwargs):
if loop is None:
loop = asyncio.get_event_loop()
pfunc = partial(func, *args, **kwargs)
return await loop.run_in_executor(executor, pfunc)
return run
@wrap
def sleep_async(delay):
time.sleep(delay)
return 'I slept asynchronously'
ou utilisez la bibliothèque aioify
% pip install aioify
puis
@aioify
def sleep_async(delay):
pass
Je ne sais pas si c'est trop tard, mais vous pouvez également utiliser un décorateur pour faire votre fonction dans un fil. TOUTEFOIS, notez qu'il s'agira toujours d'un blocage non coopératif contrairement à async qui est un blocage coopératif.
def wrap(func):
from concurrent.futures import ThreadPoolExecutor
pool=ThreadPoolExecutor()
@wraps(func)
async def run(*args, loop=None, executor=None, **kwargs):
if loop is None:
loop = asyncio.get_event_loop()
future=pool.submit(func, *args, **kwargs)
return asyncio.wrap_future(future)
return run