Что такое Deadlock в многопоточности?

Ответ

Deadlock (взаимная блокировка) — это ситуация в многопоточных или многопроцессных системах, когда два или более потока/процесса бесконечно ожидают ресурсы, захваченные друг другом, и ни один из них не может продолжить выполнение. Это приводит к зависанию системы или ее части.

Пример Deadlock на Python с использованием threading:

import threading
import time

lock1 = threading.Lock()
lock2 = threading.Lock()

def thread1_func():
    print("Thread 1: Попытка захватить lock1...")
    with lock1:
        print("Thread 1: lock1 захвачен. Попытка захватить lock2...")
        time.sleep(0.1) # Даем время другому потоку захватить lock2
        with lock2:
            print("Thread 1: lock2 захвачен.")
    print("Thread 1: Ресурсы освобождены.")

def thread2_func():
    print("Thread 2: Попытка захватить lock2...")
    with lock2:
        print("Thread 2: lock2 захвачен. Попытка захватить lock1...")
        time.sleep(0.1) # Даем время другому потоку захватить lock1
        with lock1:
            print("Thread 2: lock1 захвачен.")
    print("Thread 2: Ресурсы освобождены.")

t1 = threading.Thread(target=thread1_func)
t2 = threading.Thread(target=thread2_func)

t1.start()
t2.start()

t1.join()
t2.join()
print("Программа завершена (если не было deadlock).")

В этом примере thread1_func пытается захватить lock1, затем lock2. thread2_func пытается захватить lock2, затем lock1. Если оба потока одновременно захватят свои первые блокировки, они будут бесконечно ждать друг друга, что приведет к deadlock.

Условия возникновения Deadlock (условия Коффмана): Deadlock возникает, если одновременно выполняются все четыре условия:

  1. Взаимное исключение (Mutual Exclusion): Ресурсы не могут быть разделены; только один поток может использовать ресурс в данный момент.
  2. Удержание и ожидание (Hold and Wait): Поток, удерживающий один или несколько ресурсов, ожидает захвата дополнительных ресурсов, которые в данный момент удерживаются другими потоками.
  3. Отсутствие вытеснения (No Preemption): Ресурсы не могут быть принудительно отобраны у потока; они могут быть освобождены только самим потоком, который их удерживает.
  4. Круговое ожидание (Circular Wait): Существует циклическая цепочка потоков, где каждый поток в цепочке ожидает ресурс, удерживаемый следующим потоком в этой цепочке.

Стратегии предотвращения и разрешения Deadlock:

  • Предотвращение: Нарушение одного или нескольких условий Коффмана.
    • Упорядочивание блокировок: Всегда захватывать ресурсы в заранее определенном порядке (например, lock1 перед lock2). Это нарушает условие кругового ожидания.
    • Захват всех ресурсов сразу: Поток запрашивает все необходимые ресурсы одновременно. Если какой-либо ресурс недоступен, он не захватывает ни одного и ждет. Нарушает условие удержания и ожидания.
  • Избегание: Динамическое принятие решений о выделении ресурсов (например, алгоритм банкира), чтобы избежать небезопасных состояний.
  • Обнаружение и восстановление: Позволяет deadlock произойти, затем обнаруживает его и восстанавливается (например, путем прерывания одного или нескольких потоков).
  • Использование таймаутов: При попытке захвата блокировки устанавливается таймаут (lock.acquire(timeout=5)). Если блокировка не получена за это время, поток может освободить уже захваченные ресурсы и повторить попытку.