Какая функция в AsyncIO запускает несколько корутин одновременно?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Какая функция в AsyncIO запускает несколько корутин одновременно?
В Python AsyncIO существует несколько способов запуска множественных корутин, и наиболее распространённые — это asyncio.gather() и asyncio.create_task().
1. asyncio.gather() — основной метод
Это самый практичный и часто используемый способ запуска нескольких корутин:
import asyncio
async def fetch_data(url):
print(f"Fetching {url}")
await asyncio.sleep(1)
return f"Data from {url}"
async def main():
# Запускаем 3 корутины одновременно
results = await asyncio.gather(
fetch_data('http://api1.com'),
fetch_data('http://api2.com'),
fetch_data('http://api3.com')
)
print(results)
# Всё выполнится за ~1 секунду, а не за 3!
asyncio.run(main())
Время выполнения: ~1 сек (параллельно), а не 3 сек (последовательно).
2. asyncio.gather() с динамическим списком
Когда количество корутин заранее неизвестно:
import asyncio
async def process_item(item):
await asyncio.sleep(1)
return f"Processed: {item}"
async def main():
items = ['a', 'b', 'c', 'd', 'e']
# Создаём список корутин
tasks = [process_item(item) for item in items]
# Запускаем все одновременно
results = await asyncio.gather(*tasks)
print(results)
# Выполнится за ~1 сек (все параллельно)
asyncio.run(main())
3. asyncio.create_task() для явного управления
Для большего контроля над задачами:
import asyncio
async def task1():
await asyncio.sleep(2)
print("Task 1 done")
return "Result 1"
async def task2():
await asyncio.sleep(1)
print("Task 2 done")
return "Result 2"
async def main():
# Создаём задачи явно
t1 = asyncio.create_task(task1())
t2 = asyncio.create_task(task2())
# Ждём оба результата
result1 = await t1
result2 = await t2
print(result1, result2)
# Task 2 done (после 1 сек)
# Task 1 done (после 2 сек)
# Выполнение: ~2 сек
asyncio.run(main())
4. asyncio.TaskGroup() — современный подход (Python 3.11+)
Новый и более безопасный способ:
import asyncio
async def fetch(url):
await asyncio.sleep(1)
return f"Data from {url}"
async def main():
async with asyncio.TaskGroup() as tg:
# Все задачи запускаются параллельно
task1 = tg.create_task(fetch('http://api1.com'))
task2 = tg.create_task(fetch('http://api2.com'))
task3 = tg.create_task(fetch('http://api3.com'))
# Выходим из контекста только когда всё завершится
print(task1.result(), task2.result(), task3.result())
# Выполнение: ~1 сек
asyncio.run(main())
5. asyncio.as_completed() для обработки по мере готовности
Когда нужна результат сразу, как только корутина завершится:
import asyncio
async def fetch(url, delay):
await asyncio.sleep(delay)
return f"Data from {url}"
async def main():
tasks = [
fetch('http://api1.com', 3),
fetch('http://api2.com', 1),
fetch('http://api3.com', 2),
]
# Обрабатываем результаты по мере готовности
for coro in asyncio.as_completed(tasks):
result = await coro
print(f"Got: {result}") # Выводит в порядке готовности
# api2 готов первым (1с), потом api3 (2с), потом api1 (3с)
asyncio.run(main())
6. asyncio.wait() для детального контроля
Наибольший контроль над ожиданием:
import asyncio
async def task(name, delay):
await asyncio.sleep(delay)
return f"{name} done"
async def main():
tasks = [
asyncio.create_task(task('Task1', 2)),
asyncio.create_task(task('Task2', 1)),
asyncio.create_task(task('Task3', 3)),
]
# Ждём первого завершённого
done, pending = await asyncio.wait(tasks, return_when=asyncio.FIRST_COMPLETED)
print(f"First done: {done.pop().result()}")
# Ждём оставшихся
done, pending = await asyncio.wait(pending)
for task in done:
print(task.result())
asyncio.run(main())
Сравнение методов
# ❌ НЕПРАВИЛЬНО — последовательное выполнение
async def wrong():
r1 = await fetch('api1') # 1 сек
r2 = await fetch('api2') # 1 сек
r3 = await fetch('api3') # 1 сек
# Всего: 3 сек!
# ✅ ПРАВИЛЬНО — параллельное выполнение
async def correct():
results = await asyncio.gather(
fetch('api1'), # 1 сек (параллельно)
fetch('api2'), # 1 сек (параллельно)
fetch('api3'), # 1 сек (параллельно)
)
# Всего: 1 сек!
Обработка исключений
import asyncio
async def failing_task():
await asyncio.sleep(0.5)
raise ValueError("Task failed!")
async def normal_task():
await asyncio.sleep(1)
return "Success"
async def main():
# Игнорировать исключения
results = await asyncio.gather(
failing_task(),
normal_task(),
return_exceptions=True # Исключения как результаты
)
print(results) # [ValueError(...), 'Success']
asyncio.run(main())
Практический пример: загрузка множества файлов
import asyncio
import aiohttp
async def download_file(session, url):
async with session.get(url) as response:
return await response.text()
async def main():
urls = [
'https://api.example.com/data1',
'https://api.example.com/data2',
'https://api.example.com/data3',
]
async with aiohttp.ClientSession() as session:
# Загружаем все параллельно
results = await asyncio.gather(*[
download_file(session, url) for url in urls
])
return results
# Запуск
data = asyncio.run(main())
Рекомендации по выбору
| Функция | Когда использовать | Примечание |
|---|---|---|
gather() | Стандартный случай | Просто и читаемо |
TaskGroup() | Python 3.11+ | Более безопасно |
create_task() | Нужен контроль | Явное управление |
as_completed() | Результаты по очереди | Обработка как готово |
wait() | Детальный контроль | Сложные сценарии |
Итоговый ответ
Основная функция — asyncio.gather(), запускает несколько корутин одновременно (конкурентно).
Альтернативы:
- asyncio.TaskGroup() (Python 3.11+)
- asyncio.create_task() для явного управления
- asyncio.as_completed() для обработки по готовности
- asyncio.wait() для детального контроля
Все они запускают корутины конкурентно (не параллельно), в рамках одного потока на одном ядре процессора.