asyncio.gather
для запуска независимых корутин параллельно.
Для ограничения времени, примените asyncio.wait_for
к каждой корутине или к asyncio.gather
.
В случае таймаута, asyncio.wait_for
вызовет asyncio.TimeoutError
, который следует обработать, чтобы корректно завершить задачу.
Чтобы использовать asyncio
для параллельного выполнения независимых задач с учетом ограничений по времени, можно применить несколько техник:
async
функции (корутины). Для их выполнения в event loop создаются asyncio.Task
объекты.
asyncio.create_task(корутина)
создает задачу, которая автоматически ставится в очередь на выполнение в event loop.
asyncio.gather
для параллельного выполнения: asyncio.gather(*задачи)
позволяет запустить несколько задач одновременно. Эта функция дождется завершения всех задач и вернет список результатов (в том же порядке, в котором задачи были переданы). Если хотя бы одна задача завершится с ошибкой (исключением), то asyncio.gather
перехватит его и повторно поднимет после завершения всех остальных задач. Можно указать return_exceptions=True
, чтобы возвращались результаты и исключения.
asyncio.wait
для управления временем выполнения и завершения задач: asyncio.wait(задачи, timeout=время_в_секундах, return_when=условие)
позволяет контролировать, сколько времени ожидать завершения задач и при каком условии вернуть управление. return_when
может принимать значения:
asyncio.FIRST_COMPLETED
: Вернет управление, как только первая задача завершится.asyncio.FIRST_EXCEPTION
: Вернет управление, как только первая задача завершится с исключением.asyncio.ALL_COMPLETED
: Вернет управление, когда все задачи завершатся (по умолчанию).asyncio.wait
возвращает кортеж из двух множеств: (done, pending)
, где done
содержит завершенные задачи, а pending
- задачи, которые не были завершены до истечения времени ожидания (или по другому условию).
asyncio.timeout
в Python 3.11+ или asyncio.wait_for
в более ранних версиях): Можно задать таймаут на выполнение *каждой* отдельной задачи, чтобы предотвратить "зависание" конкретной корутины.
asyncio.timeout(время_в_секундах, корутина)
позволяет выполнить корутину с ограничением по времени. Если корутина не завершится в течение указанного времени, будет поднято исключение asyncio.TimeoutError
.asyncio.wait_for(корутина, timeout=время_в_секундах)
. Этот метод выполняет аналогичную функцию, но немного менее элегантен с точки зрения синтаксиса.task.cancel()
): Если задача не завершилась в течение отведенного времени (например, после использования asyncio.wait
), ее можно отменить с помощью task.cancel()
. Важно отметить, что отмена задачи – это запрос, а не приказ. Корутина должна корректно обрабатывать исключение asyncio.CancelledError
, которое будет поднято в отмененной задаче, и освобождать ресурсы (например, закрывать соединения).
Пример кода:
import asyncio
async def my_task(task_id, delay):
try:
print(f"Задача {task_id}: начало (задержка {delay} сек)")
await asyncio.sleep(delay)
print(f"Задача {task_id}: завершена")
return f"Результат задачи {task_id}"
except asyncio.CancelledError:
print(f"Задача {task_id}: отменена")
return None # или другое значение по умолчанию
except Exception as e:
print(f"Задача {task_id}: произошла ошибка: {e}")
return None
async def main():
tasks = [
asyncio.create_task(my_task(1, 2)),
asyncio.create_task(my_task(2, 5)),
asyncio.create_task(my_task(3, 1))
]
try:
done, pending = await asyncio.wait(tasks, timeout=3, return_when=asyncio.FIRST_COMPLETED)
print("Завершенные задачи:")
for task in done:
try:
result = task.result() # Может поднять исключение, если задача завершилась с ошибкой
print(f" - {result}")
except asyncio.CancelledError:
print(f" - Задача отменена")
except Exception as e:
print(f" - Задача завершилась с ошибкой: {e}")
print("Ожидающие задачи:")
for task in pending:
print(f" - Отмена задачи")
task.cancel() # Запрашиваем отмену
try:
await task # Дожидаемся отмены (необязательно, но рекомендуется)
except asyncio.CancelledError:
print(" - Задача успешно отменена")
except Exception as e:
print(f"Произошла ошибка в main: {e}")
if __name__ == "__main__":
asyncio.run(main())
В этом примере:
asyncio.wait
используется для ожидания завершения задач в течение 3 секунд или до завершения первой задачи.Важно: Не забывайте обрабатывать asyncio.CancelledError
в ваших корутинах, чтобы корректно освобождать ресурсы при отмене задачи.