Что такое DeadLock

«Что такое DeadLock» — вопрос из категории Многопоточность, который задают на 43% собеседований C/C++ Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

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

Классический пример на C++ с std::mutex:

#include <thread>
#include <mutex>
#include <chrono>

std::mutex mutex1, mutex2;

void thread1_work() {
    std::lock_guard<std::mutex> lock1(mutex1); // Захватываем mutex1
    std::this_thread::sleep_for(std::chrono::milliseconds(1)); // Имитация работы
    std::lock_guard<std::mutex> lock2(mutex2); // Блокировка: ждём mutex2, который у thread2
    // Критическая секция 1&2
}

void thread2_work() {
    std::lock_guard<std::mutex> lock2(mutex2); // Захватываем mutex2
    std::this_thread::sleep_for(std::chrono::milliseconds(1));
    std::lock_guard<std::mutex> lock1(mutex1); // Блокировка: ждём mutex1, который у thread1
    // Критическая секция 2&1
}
// Запуск t1 и t2 приведёт к deadlock.

Способы предотвращения, которые я применяю:

  1. Упорядоченная блокировка: Всегда захватывать мьютексы в одном и том же глобальном порядке (напр., сначала mutex1, потом mutex2).
  2. Использование std::lock: Стандартная функция, которая безопасно захватывает несколько мьютексов, избегая deadlock.
    std::lock(mutex1, mutex2); // Атомарно захватывает оба
    std::lock_guard<std::mutex> lk1(mutex1, std::adopt_lock);
    std::lock_guard<std::mutex> lk2(mutex2, std::adopt_lock);
  3. Избегание вложенных блокировок: Перепроектирование кода, чтобы уменьшить необходимость удерживать несколько мьютексов.
  4. Использование std::scoped_lock (C++17): Более современная и безопасная замена std::lock_guard для нескольких мьютексов.
    std::scoped_lock lock(mutex1, mutex2); // Deadlock-free захват

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