Можно ли попасть в deadlock на одном мьютексе?

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

Ответ

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

Однако, можно вызвать самоблокировку (self-deadlock), если поток попытается повторно захватить нерекурсивный мьютекс, который он уже удерживает.

#include <mutex>

std::mutex mtx;

void problematic_function() {
    mtx.lock();
    // ... какой-то код ...
    mtx.lock(); // Поток заблокируется здесь, ожидая самого себя -> deadlock
    mtx.unlock();
    mtx.unlock();
}

Решение:

  1. Использовать std::recursive_mutex, который позволяет одному потоку захватывать его несколько раз.
  2. Перепроектировать код, чтобы избежать повторного захвата. Часто это признак плохой структуры.
  3. Использовать std::lock_guard или std::unique_lock с std::defer_lock и std::try_lock для безопасных попыток захвата.
// Решение с recursive_mutex
std::recursive_mutex rec_mtx;

void safe_function() {
    std::lock_guard<std::recursive_mutex> lock(rec_mtx);
    // ... можно вызывать другую функцию, которая тоже захватывает этот мьютекс
}