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

98 lines
4.5 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 -*-
"""Лабораторная работа 5: синхронизация и гонки данных.
Заполните функции ниже. ИНТЕРФЕЙСЫ МЕНЯТЬ НЕЛЬЗЯ — по ним работают
автотесты (tests/test_lab05.py).
Запуск из корня курса:
python lab05_sync/solution.py
"""
import os
import queue
import sys
import threading
import time
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
from common.tasks import io_fetch
# TODO: впишите свой номер в журнале (1..20) — используется в main()
VARIANT_NUMBER = 0
def race_demo(n_threads: int, n_increments: int):
"""Гонка данных БЕЗ синхронизации.
n_threads потоков, каждый увеличивает общий счётчик n_increments раз.
counter += 1 не атомарен! Чтобы гонка проявилась надёжно, после чтения
счётчика добавьте time.sleep(0) — уступает GIL.
Вернуть (фактический_счётчик, ожидаемый = n_threads * n_increments).
"""
# TODO: общий счётчик (список [0] или nonlocal), потоки, start/join
raise NotImplementedError
def race_fixed(n_threads: int, n_increments: int):
"""То же, но с threading.Lock. Вернуть (фактический_счётчик, ожидаемый).
Результат обязан быть точным ПРИ ЛЮБЫХ запусках.
"""
raise NotImplementedError
def semaphore_limiter(items: int, delay: float, max_concurrent: int):
"""Выполнить items запросов io_fetch(item, delay) на потоках,
но не более max_concurrent одновременно (threading.Semaphore).
Вернуть (список_результатов, elapsed_seconds), упорядочен по item.
Подсказка: results[idx] — каждый воркер пишет в СВОЮ ячейку списка.
"""
raise NotImplementedError
def producer_consumer(n_producers: int, n_consumers: int, n_items: int):
"""Схема producer-consumer на queue.Queue.
Производители суммарно кладут задачи со значениями 0..n_items-1
(распределите между производителями как удобно, но все значения ровно
по одному разу). Потребители забирают и «обрабатывают» (суммируют).
Программа обязана завершиться сама: маркеры конца (None) — по одному
на потребителя. Вернуть (обработано_задач, сумма_значений).
"""
# TODO: queue.Queue() для задач и результатов; producers кладут;
# consumers берут до None; join всех; собрать результаты
raise NotImplementedError
def main():
print("=== 1. Гонка данных ===")
for attempt in range(3):
got, expected = race_demo(8, 100_000)
print(f"попытка {attempt + 1}: {got:,} из {expected:,} "
f"(потеряно {expected - got:,})")
got, expected = race_fixed(8, 100_000)
print(f"с Lock: {got:,} из {expected:,} (всегда точно)")
print("\n=== 2. Семафор: лимит соединений ===")
v_items, v_delay = 24, 0.05
for mc in (2, 4, 8):
res, t = semaphore_limiter(v_items, v_delay, mc)
ideal = v_items / mc * v_delay
print(f"max_concurrent={mc}: {len(res)} запросов за {t:.2f} c "
f"(идеал ≈ {ideal:.2f} c)")
print("\n=== 3. Producer–consumer ===")
t0 = time.perf_counter()
processed, total_sum = producer_consumer(2, 3, 30)
print(f"производителей=2, потребителей=3, задач=30: "
f"обработано {processed}, сумма {total_sum}, "
f"время {time.perf_counter() - t0:.2f} c")
print("Если программа зависла — дедлок: проверьте маркеры конца!")
# TODO: в отчёт — несколько запусков race_demo (недетерминизм!),
# цена Lock, таблицу semaphore_limiter, схему producer-consumer.
if __name__ == "__main__":
main()