Загрузка данных


"""
Лабораторная работа №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 никогда не произойдёт. Другие способы: запрашивать все ресурсы сразу (нарушение удержания и ожидания) или разрешить принудительное освобождение ресурсов (нарушение отсутствия вытеснения).