# -*- coding: utf-8 -*- """Демо: гонка данных — потерянные обновления счётчика. Запуск из корня курса: python lab05_sync/examples/01_race_demo.py counter += 1 — три операции (чтение, инкремент, запись). Два потока читают одно значение, оба пишут — одно увеличение теряется. Lock устраняет гонку. """ import os import sys import threading import time sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..", ".."))) N_THREADS = 8 N_INCR = 100_000 def worker_unsafe(counter): for _ in range(N_INCR): value = counter[0] # ЧТЕНИЕ time.sleep(0) # усиливает переключения: GIL уступается counter[0] = value + 1 # ЗАПИСЬ (могла потеряться) def worker_safe(counter, lock): for _ in range(N_INCR): with lock: # критическая секция value = counter[0] counter[0] = value + 1 def main(): expected = N_THREADS * N_INCR counter = [0] threads = [threading.Thread(target=worker_unsafe, args=(counter,)) for _ in range(N_THREADS)] t0 = time.perf_counter() for t in threads: t.start() for t in threads: t.join() unsafe_time = time.perf_counter() - t0 print(f"без синхронизации: {counter[0]:>10,} из {expected:,} " f"(потеряно {expected - counter[0]:,}) за {unsafe_time:.2f} c") counter = [0] lock = threading.Lock() threads = [threading.Thread(target=worker_safe, args=(counter, lock)) for _ in range(N_THREADS)] t0 = time.perf_counter() for t in threads: t.start() for t in threads: t.join() safe_time = time.perf_counter() - t0 print(f"с Lock: {counter[0]:>10,} из {expected:,} " f"(идеально) за {safe_time:.2f} c") print(f"\nРазница времени: {(safe_time - unsafe_time) / unsafe_time * 100:+.0f}%") print("(Lock может даже ускорить: без него потоки чаще конфликтуют)") print("Запустите ещё раз — потерянные обновления будут другими:") print("гонка недетерминирована, этим она и опасна.") if __name__ == "__main__": main()