← Назад к вопросам
Сколько нужно Mutex, чтобы создать Deadlock?
1.3 Junior🔥 121 комментариев
#Многопоточность и синхронизация
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI28 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Минимальное количество мьютексов для дедлока
Теоретический ответ: Для создания дедлока необходимо минимум 2 мьютекса и 2 потока. Это фундаментальное условие, которое встречается во всех операционных системах и многопоточных приложениях.
Почему именно 2 мьютекса?
Дедлок (deadlock) — это состояние, при котором два или более процесса взаимно ждут друг друга. Невозможно создать дедлок с одним мьютексом, так как никакого циклического ожидания не возникает.
Условия дедлока (Coffman conditions):
- Взаимное исключение — ресурс может использовать только один процесс
- Удержание и ожидание — процесс держит ресурс и ждёт другого
- Отсутствие вытеснения — ресурс нельзя отобрать, только отпустить
- Циклическое ожидание — А ждёт Б, Б ждёт В, ..., Z ждёт А
Классический пример с двумя мьютексами
#include <thread>
#include <mutex>
#include <chrono>
std::mutex mutex1, mutex2;
void thread1_func() {
std::lock_guard<std::mutex> lock1(mutex1);
std::this_thread::sleep_for(std::chrono::milliseconds(10));
std::lock_guard<std::mutex> lock2(mutex2); // DEADLOCK!
std::cout << "Thread 1 completed" << std::endl;
}
void thread2_func() {
std::lock_guard<std::mutex> lock2(mutex2);
std::this_thread::sleep_for(std::chrono::milliseconds(10));
std::lock_guard<std::mutex> lock1(mutex1); // DEADLOCK!
std::cout << "Thread 2 completed" << std::endl;
}
int main() {
std::thread t1(thread1_func);
std::thread t2(thread2_func);
t1.join();
t2.join();
return 0;
}
Как избежать дедлока
1. Упорядочение ресурсов — всегда захватывать в одном порядке:
void thread1_func() {
std::lock_guard<std::mutex> lock1(mutex1);
std::lock_guard<std::mutex> lock2(mutex2);
// Работа с обоими ресурсами
}
void thread2_func() {
std::lock_guard<std::mutex> lock1(mutex1); // ТОТ ЖЕ порядок!
std::lock_guard<std::mutex> lock2(mutex2);
// Работа с обоими ресурсами
}
2. Использование std::lock для атомного захвата:
void thread1_func() {
std::lock(mutex1, mutex2); // Атомно захватываем оба
std::lock_guard<std::mutex> lock1(mutex1, std::adopt_lock);
std::lock_guard<std::mutex> lock2(mutex2, std::adopt_lock);
}
3. Установка тайм-аутов:
std::timed_mutex timed_mutex1, timed_mutex2;
void thread1_func() {
if (timed_mutex1.try_lock_for(std::chrono::milliseconds(100))) {
if (timed_mutex2.try_lock_for(std::chrono::milliseconds(100))) {
// Работа...
timed_mutex2.unlock();
} else {
timed_mutex1.unlock();
}
}
}
Практические рекомендации
- Минимизируй время захвата — освобождай мьютекс как можно скорее
- Избегай вложенного захвата — используй
std::lockесли нужны несколько мьютексов - Используй higher-level примитивы — condition variables, readers-writers locks
- Проектируй с нуля — планируй синхронизацию заранее
- Тестируй многопоточность — дедлоки проявляются непредсказуемо
Итого: для дедлока нужно минимум 2 мьютекса и правильное (неправильное!) расписание потоков.