tutorial run programming loop has espaƱol attribute asyncio async python python-3.x python-3.5 coroutine python-asyncio

python - run - asyncio.ensure_future vs. BaseEventLoop.create_task vs. simple coroutine?



python async api (4)

He visto varios tutoriales básicos de Python 3.5 sobre asyncio haciendo la misma operación en varios sabores. En este código:

import asyncio async def doit(i): print("Start %d" % i) await asyncio.sleep(3) print("End %d" % i) return i if __name__ == ''__main__'': loop = asyncio.get_event_loop() #futures = [asyncio.ensure_future(doit(i), loop=loop) for i in range(10)] #futures = [loop.create_task(doit(i)) for i in range(10)] futures = [doit(i) for i in range(10)] result = loop.run_until_complete(asyncio.gather(*futures)) print(result)

Las tres variantes anteriores que definen la variable de futures logran el mismo resultado; La única diferencia que puedo ver es que con la tercera variante la ejecución está fuera de orden (lo que no debería importar en la mayoría de los casos). ¿Hay alguna otra diferencia? ¿Hay casos en los que no puedo usar la variante más simple (lista simple de corutinas)?


Nota: Solo válido para Python 3.7 (para Python 3.5 consulte la respuesta anterior ).

De los documentos oficiales:

asyncio.create_task (agregado en Python 3.7) es la forma preferible de generar nuevas tareas en lugar de ensure_future() .

Detalle:

Entonces, en Python 3.7 en adelante, hay 2 funciones de envoltura de nivel superior (similares pero diferentes):

Bueno, ambas funciones de contenedor lo ayudarán a llamar a BaseEventLoop.create_task . La única diferencia es ensure_future que el ensure_future acepte cualquier objeto que awaitable y lo ayude a convertirlo en un futuro. Y también puede proporcionar su propio parámetro event_loop en ensure_future . Y dependiendo de si necesita esas capacidades o no, simplemente puede elegir qué envoltura usar.


Información actual:

A partir de Python 3.7, se agregó la función de alto nivel asyncio.create_task(coro) para este propósito.

En su lugar, debe usar otras formas de crear tareas a partir de tiempos de ejecución. Sin embargo, si necesita crear una tarea de espera arbitraria, debe usar asyncio.ensure_future(obj) .

Información antigua:

ensure_future vs create_task

ensure_future es un método para crear Task desde la coroutine . Crea tareas de diferentes maneras basadas en argumentos (incluido el uso de create_task para corutinas y objetos similares al futuro).

create_task es un método abstracto de AbstractEventLoop . Los diferentes bucles de eventos pueden implementar esta función de diferentes maneras.

Debería usar ensure_future para crear tareas. Necesitará create_task solo si va a implementar su propio tipo de bucle de eventos.

Upd:

@ bj0 señaló la respuesta de Guido sobre este tema:

El punto de ensure_future() es si tiene algo que podría ser una rutina o un Future (este último incluye una Task porque es una subclase de Future ), y desea poder llamar a un método que solo está definido en Future (probablemente el único ejemplo útil sea cancel() ). Cuando ya es un Future (o Task ), esto no hace nada; cuando es una rutina, la envuelve en una Task .

Si sabe que tiene una rutina y desea que se programe, la API correcta para usar es create_task() . El único momento en que debería llamar a ensure_future() es cuando proporciona una API (como la mayoría de las API de asyncio) que acepta una rutina o un Future y necesita hacer algo que requiera que tenga un Future .

y después:

Al final, todavía creo que ensure_future() es un nombre apropiadamente oscuro para una pieza de funcionalidad que rara vez se necesita. Al crear una tarea desde una rutina, debe usar el loop.create_task() apropiadamente nombrado. Tal vez debería haber un alias para ese asyncio.create_task() ?

Es sorprendente para mi. Mi principal motivación para usar ensure_future todo el tiempo fue que es una función de nivel superior en comparación con el miembro create_task del bucle (la discusión contains algunas ideas como agregar asyncio.spawn o asyncio.create_task ).

También puedo señalar que, en mi opinión, es bastante conveniente usar una función universal que pueda manejar cualquier Awaitable lugar de solo corutinas.

Sin embargo, la respuesta de Guido es clara: "Al crear una tarea a partir de una rutina, debe usar el loop.create_task() ".

¿Cuándo las corutinas deben envolverse en tareas?

Ajustar la rutina en una tarea: es una forma de comenzar esta rutina "en segundo plano". Aquí hay un ejemplo:

import asyncio async def msg(text): await asyncio.sleep(0.1) print(text) async def long_operation(): print(''long_operation started'') await asyncio.sleep(3) print(''long_operation finished'') async def main(): await msg(''first'') # Now you want to start long_operation, but you don''t want to wait it finised: # long_operation should be started, but second msg should be printed immediately. # Create task to do so: task = asyncio.ensure_future(long_operation()) await msg(''second'') # Now, when you want, you can await task finised: await task if __name__ == "__main__": loop = asyncio.get_event_loop() loop.run_until_complete(main())

Salida:

first long_operation started second long_operation finished

Puede reemplazar asyncio.ensure_future(long_operation()) con solo await long_operation() para sentir la diferencia.


create_task()

  • acepta corutinas,
  • devuelve Tarea,
  • Se invoca en el contexto del bucle.

ensure_future()

  • acepta futuros, corutinas, objetos esperables,
  • devuelve Tarea (o Futuro si el Futuro pasó).
  • si el argumento dado es una rutina, usa create_task ,
  • Se puede pasar el objeto de bucle.

Como puede ver, create_task es más específico.

función async sin create_task o allow_future

La función async invocación simple devuelve la rutina

>>> async def doit(i): ... await asyncio.sleep(3) ... return i >>> doit(4) <coroutine object doit at 0x7f91e8e80ba0>

Y dado que la gather bajo el capó asegura ( ensure_future ) que los ensure_future son futuros, explícitamente, ensure_future es redundante.

Pregunta similar ¿ Cuál es la diferencia entre loop.create_task, asyncio.async / allow_future y Task?


para su ejemplo, los tres tipos se ejecutan de forma asincrónica. la única diferencia es que, en el tercer ejemplo, generó previamente las 10 corutinas y las envió al ciclo juntas. así que solo el último da salida al azar.