Ответ
Нет, классический deadlock (взаимная блокировка) требует циклической зависимости как минимум между двумя потоками и двумя ресурсами. На одном мьютексе это невозможно.
Однако, можно вызвать самоблокировку (self-deadlock), если поток попытается повторно захватить нерекурсивный мьютекс, который он уже удерживает.
#include <mutex>
std::mutex mtx;
void problematic_function() {
mtx.lock();
// ... какой-то код ...
mtx.lock(); // Поток заблокируется здесь, ожидая самого себя -> deadlock
mtx.unlock();
mtx.unlock();
}
Решение:
- Использовать
std::recursive_mutex, который позволяет одному потоку захватывать его несколько раз. - Перепроектировать код, чтобы избежать повторного захвата. Часто это признак плохой структуры.
- Использовать
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);
// ... можно вызывать другую функцию, которая тоже захватывает этот мьютекс
} Ответ 18+ 🔞
А, ну это классика, ёпта! Смотри, тут часто путают: чтобы по-настоящему заклинить систему, как в том анекдоте про двух упрямых козлов на мосту, нужно минимум два потока и два ресурса, которые они друг у дружки пытаются отжать. А на одном-то мьютексе настоящий deadlock не устроишь — это как пытаться самому себе очередь в туалете занять, блядь.
Хотя подожди... самому себе ногу наступить — запросто! Вот смотри, если поток возьмёт обычный мьютекс, а потом, не отпустив, опять попробует его же и схватить — всё, пиши пропало. Он сам себя будет ждать до второго пришествия, ядрёна вошь. Это называется самоблокировка.
#include <mutex>
std::mutex mtx;
void problematic_function() {
mtx.lock();
// ... какой-то код ...
mtx.lock(); // Вот тут-то он и обосрётся! Сидит и ждёт, пока какой-то добрый молодец мьютекс отпустит. А добрый молодец — это он же и есть. Замкнутый круг, пиздец.
mtx.unlock();
mtx.unlock();
}
Ну и что с этим безобразием делать? Вариантов несколько, чувак.
-
Рекурсивный мьютекс (
std::recursive_mutex). Это как разрешить самому себе заходить в свой же холодильник сколько угодно раз. Захватил — иди кушай. Захотел ещё раз — да похуй, ты же уже внутри. Главное, чтобы отпустил потом ровно столько же раз, сколько взял, а то другим не достанется. -
Переделать архитектуру. Чаще всего, если тебе вдруг захотелось второй раз взяться за свой же замок — это тревожный звоночек. Может, код пора пересмотреть, чтобы такой ерунды не возникало? Подозрение ёбать чувствую к таким конструкциям.
-
Умные обёртки и осторожные попытки. Использовать
std::unique_lockс флагами вродеstd::defer_lockи методtry_lock(). Это как аккуратно постучать в дверь: "Эй, я тут, можно я?" — и если там уже ты сидишь, то не ломиться, а пойти чайку попить.
// Вариант с рекурсивным — просто и для ленивых
std::recursive_mutex rec_mtx;
void safe_function() {
std::lock_guard<std::recursive_mutex> lock(rec_mtx);
// Теперь можешь хоть тысячу раз функции вызывать, которые тоже этот мьютекс берут — волнение ёбать нулевое.
}
Короче, вывод простой: на одном мьютексе полный взаимный клин не выйдет, но вот сесть на собственный же хуй и зависнуть — элементарно. Будь осторожен, не делай как тот Гамлет, который сам себя отрицал до посинения.