Что такое DeadLock

Ответ

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 захват

Ответ 18+ 🔞

Да ты посмотри, какая хитрая жопа эта deadlock-ситуация! Представь себе, два потока уперлись, как бараны, и оба ждут, пока другой отпустит свой ресурс. Получается этакая циклическая зависимость, ёпта, из которой нихуя не вырваться — они так и будут висеть до скончания времён, если ты, конечно, не прибьёшь процесс.

Вот тебе классический пример на C++, прям как в жизни:

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

std::mutex mutex1, mutex2;

void thread1_work() {
    std::lock_guard<std::mutex> lock1(mutex1); // Хватаем первый мьютекс
    std::this_thread::sleep_for(std::chrono::milliseconds(1)); // Немного потягиваем кофе
    std::lock_guard<std::mutex> lock2(mutex2); // БЛОКИРОВКА: ждём второй, а он у второго потока!
    // Критическая секция 1&2
}

void thread2_work() {
    std::lock_guard<std::mutex> lock2(mutex2); // Хватаем второй мьютекс
    std::this_thread::sleep_for(std::chrono::milliseconds(1));
    std::lock_guard<std::mutex> lock1(mutex1); // БЛОКИРОВКА: ждём первый, а он у первого!
    // Критическая секция 2&1
}
// Запустишь t1 и t2 — и всё, накрылся медным тазом, взаимная блокировка.

Чтобы не было тебе хиросимы и нагасаки в коде, вот как я с этим борюсь:

  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::scoped_lock lock(mutex1, mutex2); // Deadlock-free, как швейцарские часы

Главное, чувак, — не бздеть, а думать головой. А то подловит тебя этот deadlock, и будешь потом сидеть с отладчиком, материться и чувствовать себя полным ебанько.

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