Что такое Deadlock (взаимная блокировка)?

Ответ

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

Классический пример на C#:

object lockA = new object();
object lockB = new object();

Thread thread1 = new Thread(() => {
    lock (lockA) {
        Thread.Sleep(100);
        // Поток 1 ждёт lockB, который удерживается потоком 2
        lock (lockB) {
            Console.WriteLine("Thread1 completed");
        }
    }
});

Thread thread2 = new Thread(() => {
    lock (lockB) {
        Thread.Sleep(100);
        // Поток 2 ждёт lockA, который удерживается потоком 1
        lock (lockA) {
            Console.WriteLine("Thread2 completed");
        }
    }
});

thread1.Start();
thread2.Start();
// Оба потока зависнут.

Условия возникновения (Коффмана):

  1. Взаимное исключение: ресурс не может использоваться более чем одним потоком одновременно.
  2. Удержание и ожидание: поток удерживает один ресурс и ждёт другой.
  3. Отсутствие вытеснения: ресурс нельзя отобрать у потока, только добровольно освободить.
  4. Циклическое ожидание: потоки образуют замкнутый цикл ожидания ресурсов.

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

  • Упорядочивание блокировок: Всегда захватывать блокировки в строго определённом глобальном порядке (например, сначала lockA, потом lockB).
  • Использование таймаутов: Monitor.TryEnter(lockObj, TimeSpan) или SemaphoreSlim.Wait(TimeSpan) для выхода из ожидания.
  • Уменьшение времени удержания блокировки: Выполнять внутри lock только минимально необходимые операции.
  • Проектирование без блокировок: Использование потокобезопасных коллекций (ConcurrentBag, ConcurrentDictionary), async/await для I/O операций или модели акторов.
  • Статический анализ: Инструменты вроде Microsoft.ConcurrencyVisualizer или Roslyn-анализаторы для поиска потенциальных deadlock'ов.

Видео-ответы