Ответ
Для классического взаимоблокирования (deadlock) необходимо как минимум два мьютекса. Ключевое условие — потоки захватывают их в разном порядке.
Пример на C++:
#include <thread>
#include <mutex>
std::mutex mtx1;
std::mutex mtx2;
void thread_a_work() {
std::lock_guard<std::mutex> lock1(mtx1); // Захват первого
// Имитация работы, во время которой нужен второй ресурс
std::this_thread::sleep_for(std::chrono::milliseconds(10));
std::lock_guard<std::mutex> lock2(mtx2); // Ожидание второго (заблокирован thread_b)
// Критическая секция
}
void thread_b_work() {
std::lock_guard<std::mutex> lock2(mtx2); // Захват второго
std::this_thread::sleep_for(std::chrono::milliseconds(10));
std::lock_guard<std::mutex> lock1(mtx1); // Ожидание первого (заблокирован thread_a)
// Критическая секция
}
int main() {
std::thread t1(thread_a_work);
std::thread t2(thread_b_work);
t1.join();
t2.join(); // Программа может зависнуть здесь из-за deadlock
return 0;
}
Как я избегаю этого:
- Всегда захватываю мьютексы в строго одинаковом порядке во всех потоках.
- Использую
std::lock()илиstd::scoped_lock(C++17) для атомарного захвата нескольких мьютексов, что исключает deadlock.// Безопасный захват std::scoped_lock lock_all(mtx1, mtx2);
Ответ 18+ 🔞
А, ну это классика жанра, ёпта! Смотри, чтобы устроить эту самую взаимную блокировку, когда все потоки повисли и ждут друг друга как идиоты, нужно как минимум два мьютекса. И главная фишка — потоки должны хватать их в разном порядке, как два упрямых барана, которые не хотят уступить дорогу.
Вот тебе наглядный пример на C++:
#include <thread>
#include <mutex>
std::mutex mtx1;
std::mutex mtx2;
void thread_a_work() {
std::lock_guard<std::mutex> lock1(mtx1); // Хватаем первый
// Немного потянем время, типа работаем
std::this_thread::sleep_for(std::chrono::milliseconds(10));
std::lock_guard<std::mutex> lock2(mtx2); // Ждём второй (а его уже схватил thread_b)
// Критическая секция
}
void thread_b_work() {
std::lock_guard<std::mutex> lock2(mtx2); // Хватаем второй
std::this_thread::sleep_for(std::chrono::milliseconds(10));
std::lock_guard<std::mutex> lock1(mtx1); // Ждём первый (а его уже схватил thread_a)
// Критическая секция
}
int main() {
std::thread t1(thread_a_work);
std::thread t2(thread_b_work);
t1.join();
t2.join(); // Программа может тут намертво встать, потому что deadlock, ебать копать!
return 0;
}
Как я с этим борюсь, чтобы не было волнения ебать:
- Просто беру и всегда хватаю мьютексы в одном и том же порядке во всех потоках. Как в очереди за колбасой в девяностые — не лезешь без очереди, и всё будет пизда рулю.
- Использую
std::lock()илиstd::scoped_lock(это с C++17), чтобы атомарно захватить несколько мьютексов сразу. Это как взять все нужные бутерброды со стола одним движением, чтобы никто не успел перехватить.// Безопасный захват, и никакой хитрой жопы std::scoped_lock lock_all(mtx1, mtx2);