Files
2026-10-07 13:06:47 +05:00

115 lines
4.6 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- coding: utf-8 -*-
"""Лабораторная работа 6: асинхронный код (asyncio).
Заполните функции ниже. ИНТЕРФЕЙСЫ МЕНЯТЬ НЕЛЬЗЯ — по ним работают
автотесты (tests/test_lab06.py).
Запуск из корня курса:
python lab06_asyncio/solution.py
"""
import asyncio
import os
import sys
import time
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
from common.benchmark import make_table
from common.tasks import get_variant
# TODO: впишите свой номер в журнале (1..20)
VARIANT_NUMBER = 0
async def fetch_async(item: int, delay: float):
"""Корутина: имитация I/O-запроса.
await asyncio.sleep(delay) — НЕ блокирует цикл событий.
Вернуть (item, item * item).
"""
# TODO: asyncio.sleep + вернуть результат
raise NotImplementedError
async def fetch_all_async(items: int, delay: float):
"""Все items запросов конкурентно через asyncio.gather.
Вернуть (список результатов, elapsed_seconds), упорядочен по item.
"""
# TODO: gather по всем item -> сортировка не нужна, gather сохраняет порядок
raise NotImplementedError
async def fetch_all_limited(items: int, delay: float, max_concurrent: int):
"""То же, но не более max_concurrent одновременно (asyncio.Semaphore).
Вернуть (список результатов, elapsed_seconds), упорядочен по item.
"""
# TODO: sem = asyncio.Semaphore(...); async with sem: await fetch_async(...)
raise NotImplementedError
async def _fetch_blocking(item: int, delay: float):
"""«Ошибка новичка»: блокирующий time.sleep внутри корутины.
time.sleep НЕ уступает управление — парализует весь event loop.
"""
time.sleep(delay)
return (item, item * item)
async def _blocking_mistake(items: int, delay: float):
"""Все запросы через gather, но с блокирующим сном. Вернуть секунды."""
# TODO: gather по _fetch_blocking, вернуть время
raise NotImplementedError
def measure_blocking_mistake(items: int, delay: float) -> float:
"""Обёртка для тестов: asyncio.run + return секунд."""
# TODO: return asyncio.run(_blocking_mistake(items, delay))
raise NotImplementedError
def main():
v = get_variant(VARIANT_NUMBER)
items, delay = v["io"]["items"], v["io"]["delay"]
print(f"Вариант {v['variant']}: I/O-нагрузка {items} запросов по {delay} c\n")
# --- 1. Async: все запросы сразу ---------------------------------------
t0 = time.perf_counter()
results, t_async = asyncio.run(fetch_all_async(items, delay))
print(f"async, без лимита: {t_async:.3f} c "
f"(упорядочено: {[r[0] for r in results] == list(range(items))})")
# --- 2. Async с лимитом --------------------------------------------------
for mc in (4, 8):
res, t_lim = asyncio.run(fetch_all_limited(items, delay, mc))
print(f"async, лимит {mc}: {t_lim:.3f} c "
f"(идеал ≈ {items / mc * delay:.2f} c)")
# --- 3. Ошибка: time.sleep в корутине -----------------------------------
t_block = measure_blocking_mistake(items, delay)
print(f"async + time.sleep (ошибка!): {t_block:.3f} c "
f"≈ последовательные {items * delay:.2f} c\n")
# --- 4. Сравнение с потоками (лаба 2) ------------------------------------
from concurrent.futures import ThreadPoolExecutor
from common.tasks import io_fetch
t0 = time.perf_counter()
with ThreadPoolExecutor(max_workers=8) as pool:
list(pool.map(io_fetch, range(items), [delay] * items))
t_threads = time.perf_counter() - t0
rows = [("async без лимита", t_async),
("async, лимит 8", t_lim),
("потоки, p=8", t_threads),
("последовательно (ошибка time.sleep)", t_block)]
print(make_table(rows, title="Сравнение подходов к I/O"))
# TODO: в отчёт — таблицу, объяснение разницы потоков и asyncio,
# ответ на вопрос «почему time.sleep убил конкурентность».
if __name__ == "__main__":
main()