# -*- 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()