← Назад к вопросам
Можно ли выполнить параллельно 10 запросов, если каждый занимает 20 секунд?
1.8 Middle🔥 161 комментариев
#Асинхронность и многопоточность
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Можно ли выполнить параллельно 10 запросов, если каждый занимает 20 секунд?
Прямой ответ: ДА, МОЖНО
Да, 10 запросов можно выполнить параллельно. Время выполнения будет примерно 20 секунд (время одного запроса), а не 200 секунд (сумма всех). Это основное преимущество асинхронного программирования и параллелизма.
Временная шкала
Последовательное выполнение (синхронное):
Запрос 1: [========] 20s
Запрос 2: [========] 20s
Запрос 3: [========] 20s
...
Запрос 10: [========] 20s
Общее время: 200 секунд
Параллельное выполнение (асинхронное/многопоточное):
Запрос 1: [========]
Запрос 2: [========]
Запрос 3: [========]
...
Запрос 10:[========]
Общее время: 20 секунд (+ небольшие затраты на управление)
Способ 1: Asyncio (рекомендуется для I/O задач)
import asyncio
import aiohttp
import time
async def fetch_data(session, url, request_number):
"""Асинхронный запрос"""
print(f"[{request_number}] Начало запроса")
async with session.get(url) as response:
# Ждём ответ (20 сек)
data = await response.json()
print(f"[{request_number}] Готово ({len(data)} байт)")
return data
async def main():
urls = [f"https://api.example.com/data/{i}" for i in range(10)]
async with aiohttp.ClientSession() as session:
# Запустить все 10 запросов параллельно
tasks = [
fetch_data(session, url, i)
for i, url in enumerate(urls)
]
results = await asyncio.gather(*tasks)
return results
if __name__ == '__main__':
start = time.time()
results = asyncio.run(main())
elapsed = time.time() - start
print(f"\nВремя выполнения: {elapsed:.1f} сек")
# Результат: ~20 сек (не 200!)
Способ 2: Многопоточность (Threading)
import threading
import requests
import time
from concurrent.futures import ThreadPoolExecutor
def fetch_data(url, request_number):
"""Синхронный запрос в отдельном потоке"""
print(f"[{request_number}] Начало")
response = requests.get(url, timeout=20) # 20 сек ожидания
print(f"[{request_number}] Готово")
return response.json()
def main():
urls = [f"https://api.example.com/data/{i}" for i in range(10)]
# ThreadPoolExecutor автоматически управляет потоками
with ThreadPoolExecutor(max_workers=10) as executor:
results = list(executor.map(
lambda item: fetch_data(item[1], item[0]),
enumerate(urls)
))
return results
if __name__ == '__main__':
start = time.time()
results = main()
elapsed = time.time() - start
print(f"\nВремя выполнения: {elapsed:.1f} сек")
# Результат: ~20 сек
Способ 3: Multiprocessing (для CPU-bound операций)
from multiprocessing import Pool
import time
def process_request(args):
"""Обработка в отдельном процессе"""
request_number, data_size = args
# Имитация запроса
time.sleep(20)
# Вычисление (CPU-bound)
result = sum(range(10_000_000))
print(f"[{request_number}] Готово: {result}")
return result
if __name__ == '__main__':
start = time.time()
with Pool(processes=10) as pool:
# Запустить 10 процессов параллельно
results = pool.map(
process_request,
[(i, 1000) for i in range(10)]
)
elapsed = time.time() - start
print(f"\nВремя выполнения: {elapsed:.1f} сек")
# Результат: ~20 сек на многоядерной системе
Важный момент: Asyncio vs Threading vs Multiprocessing
| Подход | Для I/O | Для CPU | Накладные расходы | Рекомендация |
|---|---|---|---|---|
| Asyncio | Отличное | Плохое | Низкие | I/O операции |
| Threading | Хорошее | Плохое (GIL) | Средние | I/O в Python |
| Multiprocessing | Хорошее | Отличное | Высокие | CPU операции |
Практический пример: параллельные API запросы
import asyncio
import aiohttp
from typing import List
class DataFetcher:
def __init__(self, max_concurrent=10):
self.max_concurrent = max_concurrent
async def fetch_all(self, urls: List[str]):
"""Загрузить все URLs параллельно"""
connector = aiohttp.TCPConnector(limit=self.max_concurrent)
timeout = aiohttp.ClientTimeout(total=20)
async with aiohttp.ClientSession(
connector=connector,
timeout=timeout
) as session:
tasks = [
self._fetch_one(session, url)
for url in urls
]
return await asyncio.gather(*tasks, return_exceptions=True)
async def _fetch_one(self, session, url):
try:
async with session.get(url) as response:
return await response.json()
except asyncio.TimeoutError:
return {'error': 'timeout'}
except Exception as e:
return {'error': str(e)}
# Использование
async def main():
fetcher = DataFetcher(max_concurrent=10)
urls = [f"https://api.example.com/user/{i}" for i in range(10)]
results = await fetcher.fetch_all(urls)
print(f"Загружено {len(results)} результатов")
return results
if __name__ == '__main__':
asyncio.run(main())
Почему это работает?
- I/O Wait - пока происходит сетевой запрос (20 сек), процесс не использует CPU
- Контекстные переключения - ОС может переключать между потоками/процессами
- Асинхронность - код может ждать несколько операций одновременно
# Визуально:
# Время: 0s -----> 10s -----> 20s
# Поток 1: [ждёт ответа от сервера...] готово
# Поток 2: [ждёт ответа...] готово
# Поток 3: [ждёт...] готово
#
# В каждый момент процессор может обработать другие операции
Ограничения в реальной жизни
# На практике время будет немного больше 20 сек:
# - Сетевые задержки
# - Сетевые пакеты
# - Переключение контекста
# - Управление памятью
# Более реальный результат: ~20-25 сек
Вывод
ДА, 10 запросов по 20 секунд каждый можно выполнить за ~20 секунд используя:
- Asyncio - лучший выбор для чистых I/O операций
- Threading - когда нужна совместимость и простота
- Multiprocessing - когда есть CPU-intensive операции
Этот принцип параллелизма - одно из самых мощных улучшений производительности в современном программировании.