¿Qué hace asyncio.create_task() ? He mirado los documentos y parece que no puedo entenderlo. Un poco de código que me confunde es este:
import asyncio async def counter_loop(x, n): for i in range(1, n + 1): print(f"Counter {x}: {i}") await asyncio.sleep(0.5) return f"Finished {x} in {n}" async def main(): slow_task = asyncio.create_task(counter_loop("Slow", 4)) fast_coro = counter_loop("Fast", 2) print("Awaiting Fast") fast_val = await fast_coro print("Finished Fast") print("Awaiting Slow") slow_val = await slow_task print("Finished Slow") print(f"{fast_val}, {slow_val}") asyncio.run(main())Esto da la siguiente salida:
001 | Awaiting Fast 002 | Counter Fast: 1 003 | Counter Slow: 1 004 | Counter Fast: 2 005 | Counter Slow: 2 006 | Finished Fast 007 | Awaiting Slow 008 | Counter Slow: 3 009 | Counter Slow: 4 010 | Finished Slow 011 | Finished Fast in 2, Finished Slow in 4No entiendo muy bien cómo funciona esto.
slow_task no poder ejecutarse hasta la finalización de fast_coro porque nunca se usó en un método asyncio.gather ?await slow_task ?Awaiting Slow después de que la rutina parece haber comenzado?gather es programar una tarea . Y create_task supuestamente crea una tarea.Una respuesta en profundidad sería muy apreciada. ¡Gracias!
También vale la pena mencionar que sé muy poco sobre Futuros.
¿Qué hace
asyncio.create_task()?
Envía la rutina para que se ejecute "en segundo plano", es decir, al mismo tiempo que la tarea actual y todas las demás tareas, cambiando entre ellas en los puntos de await . Devuelve un identificador esperable llamado "tarea" que también puede usar para cancelar la ejecución de la rutina.
Es una de las primitivas centrales de asyncio, el equivalente asyncio de iniciar un hilo. (En la misma analogía, esperar la tarea con await es el equivalente a unirse a un hilo).
¿No debería
slow_taskno poder ejecutarse hasta que se completefast_coro
No, porque usaste explícitamente create_task para iniciar slow_task en segundo plano. ¿Habías escrito algo como:
slow_coro = counter_loop("Slow", 4) fast_coro = counter_loop("Fast", 2) fast_val = await fast_coro ... de hecho, slow_coro no se ejecutaría porque nadie lo habría enviado aún al bucle de eventos. Pero create_task hace exactamente eso: enviarlo al ciclo de eventos para que se ejecute al mismo tiempo que otras tareas, el punto de cambio es any await .
porque nunca se usó en un método
asyncio.gather?
asyncio.gather no es la única forma de lograr la concurrencia en asyncio. Es solo una función de utilidad que hace que sea más fácil esperar a que se completen varias corrutinas y enviarlas al bucle de eventos al mismo tiempo. create_task solo hace el envío, probablemente debería haberse llamado start_coroutine o algo así.
¿Por qué tenemos que esperar a
slow_task?
No tenemos que hacerlo, solo sirve para esperar a que ambas corrutinas terminen limpiamente. El código también podría haber esperado asyncio.sleep() o algo así. Regresar desde main() (y el bucle de eventos) inmediatamente con algunas tareas aún pendientes también habría funcionado, pero habría impreso un mensaje de advertencia que indica un posible error. Esperar (o cancelar) la tarea antes de detener el ciclo de eventos es simplemente más limpio.
¿Qué es realmente una tarea?
Es una construcción asyncio que rastrea la ejecución de una rutina en un ciclo de eventos concreto. Cuando llama a create_task , envía una rutina para su ejecución y recibe un identificador. Puede esperar este identificador cuando realmente necesite el resultado, o nunca puede esperarlo, si no le importa el resultado. Este identificador es la tarea y se hereda de Future , lo que lo hace disponible y también proporciona la interfaz basada en devolución de llamada de nivel inferior, como add_done_callback .