tutorial run español asyncio async python python-3.5 python-asyncio

run - Python "dispara y olvida" asíncrono/espera



process async python (3)

A veces hay que realizar una operación asincrónica no crítica, pero no quiero esperar a que se complete. En la implementación de rutina de Tornado puede "disparar y olvidar" una función asincrónica simplemente omitiendo la palabra clave de yield .

He estado tratando de descubrir cómo "disparar y olvidar" con la nueva sintaxis async / await lanzada en Python 3.5. Por ejemplo, un fragmento de código simplificado:

async def async_foo(): print("Do some stuff asynchronously here...") def bar(): async_foo() # fire and forget "async_foo()" bar()

Sin embargo, lo que sucede es que bar() nunca se ejecuta y en su lugar recibimos una advertencia de tiempo de ejecución:

RuntimeWarning: coroutine ''async_foo'' was never awaited async_foo() # fire and forget "async_foo()"


Esta no es una ejecución completamente asíncrona, pero quizás run_in_executor() sea ​​adecuado para usted.

def fire_and_forget(task, *args, **kwargs): loop = asyncio.get_event_loop() if callable(task): return loop.run_in_executor(None, task, *args, **kwargs) else: raise TypeError(''Task must be a callable'') def foo(): #asynchronous stuff here fire_and_forget(foo)


Gracias Sergey por la sucinta respuesta. Aquí está la versión decorada de la misma.

import asyncio import time def fire_and_forget(f): def wrapped(*args, **kwargs): return asyncio.get_event_loop().run_in_executor(None, f, *args, *kwargs) return wrapped @fire_and_forget def foo(): time.sleep(1) print("foo() completed") print("Hello") foo() print("I didn''t wait for foo()")

Produce

>>> Hello >>> foo() started >>> I didn''t wait for foo() >>> foo() completed


Upd:

Reemplace asyncio.ensure_future con asyncio.create_task todas partes si está usando Python> = 3.7 Es la forma más nueva y mejor de generar tareas .

asyncio.Task para "disparar y olvidar"

De acuerdo con los documentos de Python para asyncio.Task es posible iniciar alguna rutina para ejecutar "en segundo plano" . La tarea creada por la function asyncio.ensure_future no bloqueará la ejecución (¡por lo tanto, la función volverá inmediatamente!). Esto parece una forma de "disparar y olvidar" como lo solicitó.

import asyncio async def async_foo(): print("async_foo started") await asyncio.sleep(1) print("async_foo done") async def main(): asyncio.ensure_future(async_foo()) # fire and forget async_foo() # btw, you can also create tasks inside non-async funcs print(''Do some actions 1'') await asyncio.sleep(1) print(''Do some actions 2'') await asyncio.sleep(1) print(''Do some actions 3'') if __name__ == ''__main__'': loop = asyncio.get_event_loop() loop.run_until_complete(main())

Salida:

Do some actions 1 async_foo started Do some actions 2 async_foo done Do some actions 3

¿Qué sucede si las tareas se ejecutan después de que se complete el ciclo de eventos?

Tenga en cuenta que asyncio espera que la tarea se complete en el momento en que se complete el ciclo de eventos. Entonces, si va a cambiar main() a:

async def main(): asyncio.ensure_future(async_foo()) # fire and forget print(''Do some actions 1'') await asyncio.sleep(0.1) print(''Do some actions 2'')

Recibirá esta advertencia una vez que finalice el programa:

Task was destroyed but it is pending! task: <Task pending coro=<async_foo() running at [...]

Para evitar que pueda esperar todas las tareas pendientes después de que se complete el ciclo de eventos:

async def main(): asyncio.ensure_future(async_foo()) # fire and forget print(''Do some actions 1'') await asyncio.sleep(0.1) print(''Do some actions 2'') if __name__ == ''__main__'': loop = asyncio.get_event_loop() loop.run_until_complete(main()) # Let''s also finish all running tasks: pending = asyncio.Task.all_tasks() loop.run_until_complete(asyncio.gather(*pending))

Mata tareas en lugar de esperarlas

A veces no desea esperar que se realicen las tareas (por ejemplo, algunas tareas pueden crearse para ejecutarse para siempre). En ese caso, puede cancelarlos () en lugar de esperarlos:

import asyncio from contextlib import suppress async def echo_forever(): while True: print("echo") await asyncio.sleep(1) async def main(): asyncio.ensure_future(echo_forever()) # fire and forget print(''Do some actions 1'') await asyncio.sleep(1) print(''Do some actions 2'') await asyncio.sleep(1) print(''Do some actions 3'') if __name__ == ''__main__'': loop = asyncio.get_event_loop() loop.run_until_complete(main()) # Let''s also cancel all running tasks: pending = asyncio.Task.all_tasks() for task in pending: task.cancel() # Now we should await task to execute it''s cancellation. # Cancelled task raises asyncio.CancelledError that we can suppress: with suppress(asyncio.CancelledError): loop.run_until_complete(task)

Salida:

Do some actions 1 echo Do some actions 2 echo Do some actions 3 echo