Загрузка данных
"""
Лабораторная работа №7
Тема: Синхронизация и гонки данных
"""
import threading
# ============================================================
# 1. RACE CONDITION
# ============================================================
print("=" * 50)
print("1. Демонстрация race condition")
print("=" * 50)
counter = 0
def race_worker():
global counter
for i in range(100000):
counter += 1
threads = []
for i in range(5):
t = threading.Thread(target=race_worker)
threads.append(t)
t.start()
for t in threads:
t.join()
print(f"Ожидалось: 500000")
print(f"Получено: {counter}")
print("-> Гонка данных!\n")
# ============================================================
# 2. ИСПРАВЛЕНИЕ С MUTEX
# ============================================================
print("=" * 50)
print("2. Исправление с помощью mutex")
print("=" * 50)
safe_counter = 0
mutex = threading.Lock()
def safe_worker():
global safe_counter
for i in range(100000):
with mutex:
safe_counter += 1
threads = []
for i in range(5):
t = threading.Thread(target=safe_worker)
threads.append(t)
t.start()
for t in threads:
t.join()
print(f"Ожидалось: 500000")
print(f"Получено: {safe_counter}")
print("-> Mutex защитил данные!\n")
# ============================================================
# 3. DEADLOCK
# ============================================================
print("=" * 50)
print("3. Демонстрация deadlock")
print("=" * 50)
lock1 = threading.Lock()
lock2 = threading.Lock()
def dead_thread1():
with lock1:
print("Поток 1: захватил lock1, жду lock2...")
with lock2:
print("Поток 1: захватил lock2")
def dead_thread2():
with lock2:
print("Поток 2: захватил lock2, жду lock1...")
with lock1:
print("Поток 2: захватил lock1")
t1 = threading.Thread(target=dead_thread1)
t2 = threading.Thread(target=dead_thread2)
t1.start()
t2.start()
t1.join(timeout=1)
t2.join(timeout=1)
if t1.is_alive() or t2.is_alive():
print("\n!!! DEADLOCK !!! Оба потока зависли\n")
# ============================================================
# 4. ПРЕДОТВРАЩЕНИЕ DEADLOCK
# ============================================================
print("=" * 50)
print("4. Предотвращение deadlock")
print("=" * 50)
def safe_thread1():
with lock1:
print("Поток 1: lock1")
with lock2:
print("Поток 1: lock2")
def safe_thread2():
with lock1:
print("Поток 2: lock1")
with lock2:
print("Поток 2: lock2")
t1 = threading.Thread(target=safe_thread1)
t2 = threading.Thread(target=safe_thread2)
t1.start()
t2.start()
t1.join()
t2.join()
print("\n-> Deadlock предотвращён!\n")
# ============================================================
# ВЫВОД
# ============================================================
print("=" * 50)
print("Вывод")
print("=" * 50)
print("""
- Race condition: неверный результат из-за параллельного доступа
- Mutex: решает проблему, давая правильный результат
- Deadlock: возникает при разном порядке захвата мьютексов
- Предотвращение: одинаковый порядок захвата
""")
1. Что такое race condition?
Race condition (состояние гонки, гонка данных) — это ситуация, когда несколько потоков одновременно обращаются к общему ресурсу и хотя бы один из них изменяет этот ресурс. Результат работы программы становится непредсказуемым и зависит от случайного порядка выполнения потоков. Например, два потока одновременно увеличивают счётчик — из-за того что операция неатомарная (чтение → изменение → запись), часть изменений теряется.
---
2. Что такое критическая секция?
Критическая секция — это участок кода, в котором происходит доступ к разделяемому ресурсу. В критической секции в любой момент времени может находиться только один поток. Если два потока одновременно войдут в критическую секцию, возникнет race condition.
---
3. Для чего нужен mutex?
Mutex (mutual exclusion — взаимное исключение) — это объект синхронизации, который обеспечивает доступ к критической секции только одного потока за раз. Mutex нужен для защиты общих данных от одновременного изменения, предотвращения race condition и организации атомарности операций над разделяемыми ресурсами.
---
4. Какие условия необходимы для deadlock?
Для возникновения deadlock (взаимной блокировки) необходимо одновременное выполнение четырёх условий:
Первое — взаимное исключение: ресурс может быть занят только одним потоком.
Второе — удержание и ожидание: поток удерживает один ресурс и одновременно ждёт другой.
Третье — отсутствие вытеснения: ресурс нельзя принудительно отнять у потока.
Четвёртое — циклическое ожидание: существует замкнутая цепочка потоков, где каждый ждёт ресурс, занятый следующим.
Если нарушено хотя бы одно условие — deadlock не возникает.
---
5. Как можно предотвращать deadlock?
Достаточно нарушить любое из четырёх условий. Самый простой и популярный способ — нарушить условие циклического ожидания. Для этого нужно захватывать все мьютексы в строго определённом порядке. Например, если все потоки сначала захватывают мьютекс A, а потом мьютекс B, то deadlock никогда не произойдёт. Другие способы: запрашивать все ресурсы сразу (нарушение удержания и ожидания) или разрешить принудительное освобождение ресурсов (нарушение отсутствия вытеснения).