Init
This commit is contained in:
commit
c394677d07
59 files changed
+5149
No files matched your search
@@ -0,0 +1,69 @@
|
||||
# -*- 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()
|
||||
@@ -0,0 +1,56 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Демо: Semaphore — лимит одновременного доступа к ресурсу.
|
||||
|
||||
Запуск из корня курса:
|
||||
python lab05_sync/examples/02_semaphore_demo.py
|
||||
|
||||
Имитация: 10 «клиентов», ресурс допускает 3 одновременных подключения.
|
||||
Семафор гарантирует: активных клиентов никогда не больше 3.
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
import threading
|
||||
import time
|
||||
|
||||
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..")))
|
||||
|
||||
MAX_CONN = 3
|
||||
N_CLIENTS = 10
|
||||
DELAY = 0.2
|
||||
|
||||
active = 0
|
||||
active_lock = threading.Lock()
|
||||
max_seen = [0]
|
||||
|
||||
|
||||
def client(idx, sem):
|
||||
global active
|
||||
with sem: # ждём свободный «слот»
|
||||
with active_lock:
|
||||
active += 1
|
||||
max_seen[0] = max(max_seen[0], active)
|
||||
print(f" клиент {idx:>2} подключился (активно: {active})")
|
||||
time.sleep(DELAY) # «работа с ресурсом»
|
||||
with active_lock:
|
||||
active -= 1
|
||||
print(f" клиент {idx:>2} отключился (активно: {active})")
|
||||
|
||||
|
||||
def main():
|
||||
sem = threading.Semaphore(MAX_CONN)
|
||||
threads = [threading.Thread(target=client, args=(i, sem))
|
||||
for i in range(N_CLIENTS)]
|
||||
t0 = time.perf_counter()
|
||||
for t in threads:
|
||||
t.start()
|
||||
for t in threads:
|
||||
t.join()
|
||||
total = time.perf_counter() - t0
|
||||
|
||||
print(f"\nмаксимум одновременно: {max_seen[0]} (лимит {MAX_CONN})")
|
||||
print(f"время: {total:.2f} c, идеал ≈ {N_CLIENTS / MAX_CONN * DELAY:.2f} c")
|
||||
print("Если max_seen > лимита — семафор сломан (такого быть не должно).")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,46 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Демо: Event — сигнал между потоками.
|
||||
|
||||
Запуск из корня курса:
|
||||
python lab05_sync/examples/03_event_demo.py
|
||||
|
||||
Потоки-«работники» ждут сигнала готовности (Event.set()), после чего
|
||||
начинают работу одновременно. Event — как стартовый пистолет.
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
import threading
|
||||
import time
|
||||
|
||||
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..")))
|
||||
|
||||
|
||||
def worker(idx, start_event, results):
|
||||
name = f"работник-{idx}"
|
||||
print(f" {name}: готов, жду сигнала")
|
||||
start_event.wait() # блокируется, пока set() не вызван
|
||||
t0 = time.perf_counter()
|
||||
time.sleep(0.1) # «работа»
|
||||
results[idx] = (name, time.perf_counter() - t0)
|
||||
print(f" {name}: закончил")
|
||||
|
||||
|
||||
def main():
|
||||
start = threading.Event()
|
||||
results = {}
|
||||
threads = [threading.Thread(target=worker, args=(i, start, results))
|
||||
for i in range(4)]
|
||||
for t in threads:
|
||||
t.start()
|
||||
|
||||
time.sleep(0.5) # «подготовка данных»
|
||||
print("главный: сигнал! Event.set()")
|
||||
start.set() # все ждущие просыпаются РАЗОМ
|
||||
|
||||
for t in threads:
|
||||
t.join()
|
||||
print(f"все закончили: {len(results)} из {len(threads)}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in new issue
Block a user