Asyncio.gather vs asyncio.wait

asyncio.gather и asyncio.wait похоже, имеют схожие функции: у меня есть куча асинхронных вещей, которые я хочу выполнить/ждать (не обязательно ждать завершения одного из них до следующего запуска). Они используют другой синтаксис и отличаются некоторыми деталями, но мне кажется очень не-pythonic для меня иметь 2 функции, которые имеют такое огромное перекрытие в функциональности. Что мне не хватает?

Ответ 1

Хотя подобные в общем случае ("запускать и получать результаты для многих задач"), каждая функция имеет некоторые специфические функции для других случаев:

asyncio.gather()

Возвращает экземпляр Future, позволяющий группировать задачи на высоком уровне:

import asyncio
from pprint import pprint

import random


async def coro(tag):
    print(">", tag)
    await asyncio.sleep(random.uniform(1, 3))
    print("<", tag)
    return tag


loop = asyncio.get_event_loop()

group1 = asyncio.gather(*[coro("group 1.{}".format(i)) for i in range(1, 6)])
group2 = asyncio.gather(*[coro("group 2.{}".format(i)) for i in range(1, 4)])
group3 = asyncio.gather(*[coro("group 3.{}".format(i)) for i in range(1, 10)])

all_groups = asyncio.gather(group1, group2, group3)

results = loop.run_until_complete(all_groups)

loop.close()

pprint(results)

Все задачи в группе могут быть отменены вызовом group2.cancel() или даже all_groups.cancel(). См. Также .gather(..., return_exceptions=True),

asyncio.wait()

Поддержка ожидающего остановки после выполнения первой задачи или после указанного таймаута, позволяющая более низкую точность операций:

import asyncio
import random


async def coro(tag):
    print(">", tag)
    await asyncio.sleep(random.uniform(0.5, 5))
    print("<", tag)
    return tag


loop = asyncio.get_event_loop()

tasks = [coro(i) for i in range(1, 11)]

print("Get first result:")
finished, unfinished = loop.run_until_complete(
    asyncio.wait(tasks, return_when=asyncio.FIRST_COMPLETED))

for task in finished:
    print(task.result())
print("unfinished:", len(unfinished))

print("Get more results in 2 seconds:")
finished2, unfinished2 = loop.run_until_complete(
    asyncio.wait(unfinished, timeout=2))

for task in finished2:
    print(task.result())
print("unfinished2:", len(unfinished2))

print("Get all other results:")
finished3, unfinished3 = loop.run_until_complete(asyncio.wait(unfinished2))

for task in finished3:
    print(task.result())

loop.close()

Ответ 2

asyncio.wait более низкий уровень, чем asyncio.gather.

Как следует из названия, asyncio.gather основном фокусируется на сборе результатов. он ждет кучу фьючерсов и возвращает свои результаты в определенном порядке.

asyncio.wait просто ждет фьючерсов. и вместо того, чтобы давать результаты напрямую, он выполняет выполненные и ожидающие решения задачи. вы должны вручную собирать значения.

Более того, вы можете указать, ждать завершения всех фьючерсов или просто wait с первым.

Ответ 3

Я также заметил, что вы можете предоставить группу сопрограмм в ожидании(), просто указав список:

result=loop.run_until_complete(asyncio.wait([
        say('first hello', 2),
        say('second hello', 1),
        say('third hello', 4)
    ]))

В то время как группировка в команде gather() выполняется путем указания нескольких сопрограмм:

result=loop.run_until_complete(asyncio.gather(
        say('first hello', 2),
        say('second hello', 1),
        say('third hello', 4)
    ))