python - retardo - ¿Cómo puedo envolver una función síncrona en una rutina de Async?
set a delay in python (3)
Estoy usando aiohttp para construir un servidor de API que envíe solicitudes de TCP a un servidor separado. El módulo que envía las solicitudes TCP es síncrono y una caja negra para mis propósitos. Así que mi problema es que estas solicitudes están bloqueando la API completa. Necesito una forma de envolver las solicitudes de módulo en una rutina asíncrona que no bloquee el resto de la API.
Entonces, solo usando el modo de sleep
como un ejemplo simple, ¿hay alguna manera de envolver de alguna manera el código síncrono que consume mucho tiempo en una computadora sin bloqueo, algo como esto:
async def sleep_async(delay):
# After calling sleep, loop should be released until sleep is done
yield sleep(delay)
return ''I slept asynchronously''
Finalmente encontré una respuesta en este hilo . El método que estaba buscando es run_in_executor . Esto permite que una función síncrona se ejecute de forma asíncrona sin bloquear un bucle de eventos.
En el ejemplo de sleep
que publiqué arriba, podría verse así:
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''
También vea la siguiente respuesta -> ¿Cómo llamamos una función normal donde se espera una coroutine?
No estoy seguro si es demasiado tarde, pero también puedes usar un decorador para hacer tu función en un hilo. AUNQUE, tenga en cuenta que seguirá siendo un bloqueo no coop a diferencia de async que es el bloqueo cooperativo.
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
¡Espero que ayude!
Puede utilizar un decorador para ajustar la versión de sincronización a una versión asíncrona.
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''
o usa el aioify
lib
% pip install aioify
entonces
@aioify
def sleep_async(delay):
pass