В чем разница между await, create_task и gather в Python
Что проверяют
Понимаете ли вы, что асинхронность в asyncio сама по себе ничего не распараллеливает: конкурентность появляется только тогда, когда корутины обёрнуты в задачи. Следом почти всегда спрашивают, что будет, если одна из задач внутри gather упадёт с исключением.
Как отвечать
Разведите три инструмента по тому, что они делают с событийным циклом:
await coro(): выполняет корутину прямо здесь и ждёт результата. Дваawaitподряд идут строго последовательно, общее время равно сумме;asyncio.create_task(coro()): оборачивает корутину вTaskи ставит её в очередь цикла. Выполняться она начнёт, как только текущий код отдаст управление на ближайшемawait, а у вас на руках объект задачи: его можно дождаться, отменить черезcancel()или проверить черезdone();- *`asyncio.gather(aws)`**: сам превращает переданные корутины в задачи, запускает их конкурентно и возвращает список результатов в порядке аргументов, а не в порядке завершения.
По исключениям: при return_exceptions=False первое исключение сразу пробрасывается в того, кто ждёт gather, но остальные задачи не отменяются и продолжают работать. С return_exceptions=True ошибки возвращаются в списке вместе с результатами. Начиная с Python 3.11 для группы задач есть asyncio.TaskGroup: при ошибке он отменяет соседей и поднимает ExceptionGroup, это более предсказуемый вариант.
Добавьте, что всё это работает в одном потоке. Выигрыш есть на ожидании ввода-вывода, а CPU-задачу или блокирующий вызов нужно уносить в asyncio.to_thread или в пул процессов.
Где спотыкаются
Пишут await fetch(a) и await fetch(b) подряд и считают, что запросы идут одновременно. Они идут друг за другом.
Вызывают create_task и не сохраняют ссылку на задачу. Цикл держит на неё только слабую ссылку, и документация прямо предупреждает, что такая задача может исчезнуть, не доработав.
Уверены, что gather отменяет остальные задачи при первой ошибке. Не отменяет, они продолжают работать в фоне. Нужна отмена соседей, берите TaskGroup.