Как управлять несколькими асинхронными задачами с помощью `asyncio.gather()`?

Используйте asyncio.gather() для одновременного запуска нескольких сопрограмм (coroutine). Он принимает список сопрограмм в качестве аргументов и возвращает одну сопрограмму, которая ждет завершения всех переданных сопрограмм. Результаты возвращаются в виде списка, в том же порядке, в котором были переданы задачи. В случае возникновения исключения в одной из задач, оно будет выброшено, если явно не обработано внутри сопрограммы. Можно использовать return_exceptions=True, чтобы собрать результаты всех задач, включая исключения.

Для управления несколькими асинхронными задачами с помощью asyncio.gather(), необходимо выполнить следующие шаги:

  1. Определите асинхронные функции (корутины): Сначала определите асинхронные функции, которые вы хотите запустить параллельно. Каждая из этих функций должна быть объявлена с использованием ключевого слова async. Например:
    
    async def task_one():
        print("Task one started")
        await asyncio.sleep(2)
        print("Task one finished")
        return "Result from task one"
    
    async def task_two():
        print("Task two started")
        await asyncio.sleep(1)
        print("Task two finished")
        return "Result from task two"
                
  2. Используйте asyncio.gather() для запуска корутин: Функция asyncio.gather() принимает одну или несколько корутин в качестве аргументов и планирует их выполнение. Она возвращает корутину, которая завершится, когда все переданные корутины завершатся. Результаты каждой корутины будут возвращены в виде списка, в том же порядке, в котором корутины были переданы в asyncio.gather().
    
    async def main():
        results = await asyncio.gather(
            task_one(),
            task_two()
        )
        print("All tasks completed")
        print("Results:", results)  # ['Result from task one', 'Result from task two']
                
  3. Обработайте результаты (опционально): После завершения asyncio.gather() вы можете обработать результаты, полученные от каждой асинхронной задачи. В примере выше, результаты помещаются в список `results`.
  4. Запустите главный цикл событий (Event Loop): Чтобы запустить асинхронный код, необходимо использовать цикл событий (Event Loop). Самый простой способ - использовать asyncio.run().
    
    asyncio.run(main())
                

Пример полного кода:


import asyncio

async def task_one():
    print("Task one started")
    await asyncio.sleep(2)
    print("Task one finished")
    return "Result from task one"

async def task_two():
    print("Task two started")
    await asyncio.sleep(1)
    print("Task two finished")
    return "Result from task two"

async def main():
    results = await asyncio.gather(
        task_one(),
        task_two()
    )
    print("All tasks completed")
    print("Results:", results)

asyncio.run(main())
    

Обработка исключений:

Если одна из задач, переданных в asyncio.gather(), вызовет исключение, по умолчанию, asyncio.gather() отменит все остальные задачи и перебросит это исключение. Чтобы избежать этого поведения и позволить другим задачам завершиться, можно передать аргумент return_exceptions=True. В этом случае, asyncio.gather() вернет список результатов, где вместо результатов задач, вызвавших исключения, будут содержаться сами исключения.


import asyncio

async def task_one():
    print("Task one started")
    await asyncio.sleep(2)
    print("Task one finished")
    return "Result from task one"

async def task_two():
    print("Task two started")
    await asyncio.sleep(1)
    raise ValueError("Something went wrong in task two")
    print("Task two finished")  # This line will not be executed
    return "Result from task two"

async def main():
    results = await asyncio.gather(
        task_one(),
        task_two(),
        return_exceptions=True  # Crucial argument!
    )
    print("All tasks either completed or failed")
    print("Results:", results) # ['Result from task one', ValueError('Something went wrong in task two')]

asyncio.run(main())

    

В этом случае важно проверить тип элементов в списке результатов, чтобы определить, вернулась ли задача успешно или вызвала исключение.

В заключение, asyncio.gather() - это мощный инструмент для параллельного выполнения нескольких асинхронных задач. Он позволяет существенно повысить производительность, особенно в задачах, связанных с ожиданием ввода-вывода, таких как сетевые запросы или чтение данных из файлов.

0