Зачем нужен gather в asyncio?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
asyncio.gather() — для параллельного выполнения асинхронных задач
asyncio.gather() — это функция для запуска нескольких асинхронных операций параллельно (на самом деле конкурентно) и ожидания их завершения. Это один из самых полезных инструментов в asyncio.
Базовый пример
import asyncio
async def fetch_data(name: str, delay: int):
"""Имитирует асинхронную операцию (HTTP запрос)"""
print(f"[{name}] Starting...")
await asyncio.sleep(delay)
print(f"[{name}] Done after {delay}s")
return f"Result from {name}"
async def main():
results = await asyncio.gather(
fetch_data("Task1", 2),
fetch_data("Task2", 1),
fetch_data("Task3", 3)
)
print("All results:", results)
asyncio.run(main())
Без gather выполнялось 6 секунд (2+1+3), с gather — только 3 секунды (maximum).
Зачем нужен gather
1. Экономия времени при параллельных I/O операциях
import aiohttp
import asyncio
async def fetch_url(session, url):
async with session.get(url) as response:
return await response.json()
async def fetch_multiple_apis():
urls = ["https://api.github.com/users/github", "https://api.github.com/users/google"]
async with aiohttp.ClientSession() as session:
results = await asyncio.gather(*[fetch_url(session, url) for url in urls])
return results
Если каждый запрос занимает 0.5 сек: последовательно 1.5 сек, с gather 0.5 сек.
2. Получение результатов в определённом порядке
gather() возвращает результаты в том же порядке, что вы передали задачи.
async def main():
task1_result, task2_result, task3_result = await asyncio.gather(
fetch_data("API1"),
fetch_data("API2"),
fetch_data("API3")
)
3. Работа с динамическим списком задач
async def process_items(items):
tasks = [process_one_item(item) for item in items]
results = await asyncio.gather(*tasks)
return results
Параметры gather
return_exceptions=True — не падать при ошибке
async def task_that_fails():
raise ValueError("Something went wrong")
async def safe_task():
return "Success"
async def main():
results = await asyncio.gather(
task_that_fails(),
safe_task(),
return_exceptions=True
)
print(results)
for i, result in enumerate(results):
if isinstance(result, Exception):
print(f"Task {i} failed: {result}")
else:
print(f"Task {i}: {result}")
Отличие от других способов
1. gather vs create_task
# gather — ждём все задачи
results = await asyncio.gather(task1, task2, task3)
# create_task — запускаем, но не ждём
task1 = asyncio.create_task(coro1)
await task1
2. gather vs wait
# gather — просто ждёт и возвращает результаты
results = await asyncio.gather(task1, task2)
# wait — контроль за процессом завершения
done, pending = await asyncio.wait([task1, task2])
Практический пример: обработка запросов к БД
import asyncpg
async def get_user_with_data(pool, user_id):
async with pool.acquire() as conn:
user = await conn.fetchrow("SELECT * FROM users WHERE id = $1", user_id)
posts = await conn.fetch("SELECT * FROM posts WHERE user_id = $1", user_id)
return {"user": user, "posts": posts}
async def get_multiple_users(pool, user_ids):
results = await asyncio.gather(
*[get_user_with_data(pool, uid) for uid in user_ids],
return_exceptions=True
)
return results
Итог
asyncio.gather() нужна для: запуска нескольких асинхронных операций параллельно, получения результатов в упорядоченном виде, удобного обработки ошибок, работы с динамическими списками задач. Без gather нужно было бы вручную создавать задачи, запускать их и ждать — это намного более громоздко.