Приведи пример DeadLock
1️⃣ Как кратко ответить
Deadlock — это ситуация, когда два или более потоков блокируют друг друга, ожидая освобождения ресурсов, которые удерживаются друг другом. Это приводит к тому, что ни один из потоков не может продолжить выполнение. Deadlock может возникнуть, если потоки захватывают несколько ресурсов в разном порядке.
2️⃣ Подробное объяснение темы
Deadlock (взаимная блокировка) — это состояние в многопоточных приложениях, когда два или более потоков не могут продолжить выполнение, потому что каждый из них ожидает освобождения ресурса, удерживаемого другим потоком. Это приводит к бесконечному ожиданию и остановке выполнения программы.
Условия возникновения Deadlock
Для возникновения deadlock должны быть выполнены следующие условия:
- Взаимное исключение: Ресурсы не могут быть разделены, и каждый ресурс может быть занят только одним потоком в данный момент времени.
- Удержание и ожидание: Поток, который уже удерживает один ресурс, может запросить дополнительные ресурсы и ждать их освобождения.
- Отсутствие принудительного освобождения: Ресурсы не могут быть принудительно отобраны у потока, они должны быть освобождены только самим потоком.
- Циклическое ожидание: Существует замкнутый цикл потоков, где каждый поток ожидает ресурс, удерживаемый следующим в цикле потоком.
Пример Deadlock в C++
Рассмотрим пример, где два потока пытаются захватить два мьютекса в разном порядке, что приводит к deadlock.
#include <iostream>
#include <thread>
#include <mutex>
// Два мьютекса, которые будут использоваться потоками
std::mutex mutex1;
std::mutex mutex2;
// Функция, выполняемая первым потоком
void threadFunction1() {
// Захватываем первый мьютекс
std::lock_guard<std::mutex> lock1(mutex1);
std::this_thread::sleep_for(std::chrono::milliseconds(100)); // Имитируем работу
// Пытаемся захватить второй мьютекс
std::lock_guard<std::mutex> lock2(mutex2);
std::cout << "Thread 1 has locked both mutexes" << std::endl;
}
// Функция, выполняемая вторым потоком
void threadFunction2() {
// Захватываем второй мьютекс
std::lock_guard<std::mutex> lock1(mutex2);
std::this_thread::sleep_for(std::chrono::milliseconds(100)); // Имитируем работу
// Пытаемся захватить первый мьютекс
std::lock_guard<std::mutex> lock2(mutex1);
std::cout << "Thread 2 has locked both mutexes" << std::endl;
}
int main() {
// Создаем два потока, которые выполняют функции threadFunction1 и threadFunction2
std::thread t1(threadFunction1);
std::thread t2(threadFunction2);
// Ожидаем завершения потоков
t1.join();
t2.join();
return 0;
}
Объяснение кода
-
std::mutex mutex1; std::mutex mutex2;: Объявление двух мьютексов, которые будут использоваться для синхронизации потоков.
-
void threadFunction1(): Функция, выполняемая первым потоком. Она сначала захватывает
mutex1, затем пытается захватитьmutex2. -
void threadFunction2(): Функция, выполняемая вторым потоком. Она сначала захватывает
mutex2, затем пытается захватитьmutex1. -
std::lock_guardstd::mutex lock1(mutex1);: Захват мьютекса
mutex1с помощьюlock_guard, который автоматически освободит мьютекс при выходе из области видимости. -
std::this_thread::sleep_for(std::chrono::milliseconds(100));: Имитирует задержку, чтобы увеличить вероятность возникновения deadlock.
-
std::thread t1(threadFunction1); std::thread t2(threadFunction2);: Создание двух потоков, которые выполняют функции
threadFunction1иthreadFunction2. -
t1.join(); t2.join();: Ожидание завершения выполнения потоков.
Как избежать Deadlock
- Упорядочивание захвата ресурсов: Всегда захватывайте ресурсы в одном и том же порядке во всех потоках.
- Использование
std::lock: Используйтеstd::lockдля захвата нескольких мьютексов одновременно, чтобы избежать deadlock. - Тайм-ауты: Используйте тайм-ауты при ожидании захвата мьютексов, чтобы избежать бесконечного ожидания.
- Избегание удержания и ожидания: Освобождайте все удерживаемые ресурсы перед захватом новых.
🔒 Подпишись на бусти автора и стань Алигатором, чтобы получить полный доступ к функционалу сайта и отслеживать свой прогресс!
Подписаться