Функция gather()

Функция asyncio.gather(*coros) одновременно запускает все awaitable-объекты, переданные как последовательность, и передаёт их для запуска в цикле событий. Функция asyncio.gather() является awaitable-объектом и запускается с оператором await. Если вы передаёте функции gather() сопрограмму, то она будет автоматически назначена как задача типа <class ‘_asyncio.Task’>.

asyncio.gather(*aws) возвращает список готовых результатов, которые были выполнены объектами ожидания. Это значит, что вы сначала настраиваете и создаёте задачи, после чего передаёте распакованный список в функцию gather(). Когда результат с задач получен, gather() вернёт список, состоящий из результатов выполнения каждой сопрограммы.

Код ниже демонстрирует, как передавать сразу несколько сопрограмм в функцию gather(). В этом коде на каждой итерации происходит одновременный запуск двух сопрограмм.

import asyncio
import random

async def two(x):
    await asyncio.sleep(random.randint(1, 3))
    print(x)

async def one(x):
    await asyncio.sleep(random.randint(1, 3))
    print(x)

async def main():
    for x in range(5):
        await asyncio.gather(one(1), two(2))

asyncio.run(main())

Для создания асинхронных парсеров вы, скорее всего, будете использовать подход, который представлен ниже. Он позволяет генерировать запуск сопрограмм в любом количестве, которое будет зависеть только от того, сколько страниц вам нужно обработать. В этом коде мы создали список lst, который генерирует 10 чисел. Эти числа мы передаём в сопрограмму one(x), предварительно создав задачу для выполнения функцией asyncio.create_task(). Как вы помните, результатом выполнения этой функции является объект <class ‘_asyncio.Task’>. Каждая созданная задача отправляется в созданный заранее список lst_tasks, который содержит десять задач. Далее этот список в распакованном виде мы передаём в функцию gather() для одновременного запуска всех задач в асинхронном стиле.

Сопрограмма one() имитирует свою деятельность простым засыпанием. Каждое её засыпание можно сравнить с отправкой запроса к серверу, ведь по сути, циклу событий нет разницы, по какой причине сопрограмма передаёт управление следующему awaitable-объекту.

import asyncio
import random

async def one(x):
    await asyncio.sleep(random.randint(1, 3))
    print(x)

async def main():
    lst = [x for x in range(10)]
    lst_tasks = []
    for x in lst:
        task = asyncio.create_task(one(x))
        lst_tasks.append(task)
    await asyncio.gather(*lst_tasks)
asyncio.run(main())

#Результат
    2
    6
    9
    0
    4
    1
    8
    5
    7
    3

Функция asyncio.gather() в некоторых случаях полезна тем, что она помогает группировать сопрограммы и запускать их одновременно. Это может пригодиться для одновременного парсинга нескольких разных сайтов. Есть точка входа — сопрограмма main(), которая выполняет сбор всех сопрограмм и их запуск. И есть сопрограммы one(), two(), three(), которые могут собирать данные с выбранных сайтов. Также стоит помнить, что в сопрограмму asyncio.gather() необходимо передавать распакованное множество. Распаковка происходит с помощью символа *.

*Под множеством здесь подразумевается не конкретный тип данных set(), а понятие множества в целом: такие множества, как list(), tuple(), set() и др.

import asyncio
import random

async def one(x):
    await asyncio.sleep(random.randint(1, 3))
    print(x)

async def two(x):
    await asyncio.sleep(random.randint(1, 3))
    print(x)

async def three(x):
    await asyncio.sleep(random.randint(1, 3))
    print(x)

async def main():
    group1 = asyncio.gather(*[one(i) for i in range(1, 10)])
    group2 = asyncio.gather(*[two(i) for i in range(1, 10)])
    group3 = asyncio.gather(*[three(i) for i in range(1, 10)])
    await asyncio.gather(group1, group2, group3)

asyncio.run(main())

#Результат
    3
    6
    8

   ...

    4
    2
    5


Добавить комментарий

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!: