Ejecutar tareas concurrentemente¶
Las tareas son una de las principales formas de interactuar con el bucle de
eventos. Las tareas envuelven a las co-rutinas y las siguen cuándo están
listas. Tareas son subclases de Future
, por lo que otras co-rutinas pueden
esperar o ellas y cada una tiene un resultado que se puede recuperar después de
que la tarea completa.
Comenzar una tarea¶
Para iniciar una tarea, usa create_task()
para crear una instancia
Task
. La tarea resultante se ejecutará como parte de las operaciones
simultáneas gestionadas por el bucle de eventos siempre que el bucle esté
corriendo y la co-rutina no retorne.
import asyncio
async def task_func():
print('in task_func')
return 'the result'
async def main(loop):
print('creating task')
task = loop.create_task(task_func())
print('waiting for {!r}'.format(task))
return_value = await task
print('task completed {!r}'.format(task))
print('return value: {!r}'.format(return_value))
event_loop = asyncio.get_event_loop()
try:
event_loop.run_until_complete(main(event_loop))
finally:
event_loop.close()
Este ejemplo espera a que la tarea devuelva un resultado antes de que la
función main()
termine.
$ python3 asyncio_create_task.py
creating task
waiting for <Task pending coro=<task_func() running at
asyncio_create_task.py:12>>
in task_func
task completed <Task finished coro=<task_func() done, defined at
asyncio_create_task.py:12> result='the result'>
return value: 'the result'
Cancelar una tarea¶
Al retener el objeto Task
devuelto de create_task()
, es posible
cancelar la operación de la tarea antes de que se complete.
import asyncio
async def task_func():
print('in task_func')
return 'the result'
async def main(loop):
print('creating task')
task = loop.create_task(task_func())
print('canceling task')
task.cancel()
print('canceled task {!r}'.format(task))
try:
await task
except asyncio.CancelledError:
print('caught error from canceled task')
else:
print('task result: {!r}'.format(task.result()))
event_loop = asyncio.get_event_loop()
try:
event_loop.run_until_complete(main(event_loop))
finally:
event_loop.close()
Este ejemplo crea y luego cancela una tarea antes de iniciar el bucle de
eventos. El resultado es una excepción CancelledError
de
run_until_complete()
.
$ python3 asyncio_cancel_task.py
creating task
canceling task
canceled task <Task cancelling coro=<task_func() running at
asyncio_cancel_task.py:12>>
caught error from canceled task
Si una tarea se cancela mientras está esperando otra operación concurrente, la
tarea es notificada de su cancelación al elevar una excepción
CancelledError
en el punto donde está esperando.
import asyncio
async def task_func():
print('in task_func, sleeping')
try:
await asyncio.sleep(1)
except asyncio.CancelledError:
print('task_func was canceled')
raise
return 'the result'
def task_canceller(t):
print('in task_canceller')
t.cancel()
print('canceled the task')
async def main(loop):
print('creating task')
task = loop.create_task(task_func())
loop.call_soon(task_canceller, task)
try:
await task
except asyncio.CancelledError:
print('main() also sees task as canceled')
event_loop = asyncio.get_event_loop()
try:
event_loop.run_until_complete(main(event_loop))
finally:
event_loop.close()
Atrapar la excepción brinda la oportunidad de limpiar el trabajo ya hecho, si es necesario.
$ python3 asyncio_cancel_task2.py
creating task
in task_func, sleeping
in task_canceller
canceled the task
task_func was canceled
main() also sees task as canceled
Crear tareas desde co-rutinas¶
La función ensure_future()
devuelve una Task
vinculada a la ejecución
de una co-rutina. Esa instancia Task
puede entonces ser pasada a otro
código, que puede esperarla sin saber cómo la co-rutina original fue construida
o llamada.
import asyncio
async def wrapped():
print('wrapped')
return 'result'
async def inner(task):
print('inner: starting')
print('inner: waiting for {!r}'.format(task))
result = await task
print('inner: task returned {!r}'.format(result))
async def starter():
print('starter: creating task')
task = asyncio.ensure_future(wrapped())
print('starter: waiting for inner')
await inner(task)
print('starter: inner returned')
event_loop = asyncio.get_event_loop()
try:
print('entering event loop')
result = event_loop.run_until_complete(starter())
finally:
event_loop.close()
Ten en cuenta que la rutina asignada a ensure_future()
no se inicia hasta
que algo use await
para permitir que se ejecute.
$ python3 asyncio_ensure_future.py
entering event loop
starter: creating task
starter: waiting for inner
inner: starting
inner: waiting for <Task pending coro=<wrapped() running at
asyncio_ensure_future.py:12>>
wrapped
inner: task returned 'result'
starter: inner returned