← Назад к вопросам

Приведи пример DeadLock

2.0 Middle🔥 161 комментариев
#Многопоточность и синхронизация

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI29 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Пример DeadLock

Что такое DeadLock?

DeadLock — ситуация, когда два или более потока бесконечно ждут друг друга и ни один не может продвинуться. Приложение зависает.

Классический пример: два мьютекса

#include <iostream>
#include <thread>
#include <mutex>
#include <chrono>

using namespace std;

mutex mtx1, mtx2;

void thread1_func() {
    cout << "Thread 1: захватываю mtx1\\n";
    lock_guard<mutex> lock1(mtx1);
    this_thread::sleep_for(chrono::milliseconds(100));
    
    cout << "Thread 1: жду mtx2\\n";
    lock_guard<mutex> lock2(mtx2);  // DEADLOCK!
}

void thread2_func() {
    cout << "Thread 2: захватываю mtx2\\n";
    lock_guard<mutex> lock2(mtx2);
    this_thread::sleep_for(chrono::milliseconds(100));
    
    cout << "Thread 2: жду mtx1\\n";
    lock_guard<mutex> lock1(mtx1);  // DEADLOCK!
}

int main() {
    thread t1(thread1_func);
    thread t2(thread2_func);
    
    t1.join();
    t2.join();
}

Что происходит:

  1. Thread 1 захватывает mtx1
  2. Thread 2 захватывает mtx2
  3. Thread 1 ждёт mtx2 (захвачен T2)
  4. Thread 2 ждёт mtx1 (захвачен T1)
  5. DEADLOCK! Оба потока зависают

Пример с переводом денег

class Account {
public:
    int balance;
    mutex m;
    
    Account(int b) : balance(b) {}
    
    void transfer(Account& to, int amount) {
        lock_guard<mutex> lock1(this->m);  // Блокирую себя
        this_thread::sleep_for(chrono::milliseconds(10));
        
        lock_guard<mutex> lock2(to.m);  // DEADLOCK!
        
        this->balance -= amount;
        to.balance += amount;
    }
};

int main() {
    Account acc1(1000), acc2(500);
    
    // Thread 1: переводит из acc1 в acc2
    thread t1(&Account::transfer, &acc1, ref(acc2), 100);
    
    // Thread 2: переводит из acc2 в acc1
    thread t2(&Account::transfer, &acc2, ref(acc1), 50);
    
    t1.join();  // Зависает!
    t2.join();
}

Порядок блокировок противоположный:

  • t1: блокирует acc1 → ждёт acc2
  • t2: блокирует acc2 → ждёт acc1
  • DEADLOCK!

Как предотвратить?

1. Всегда блокировать в одном порядке

void transfer(Account& to, int amount) {
    // Блокируем в порядке адресов
    Account* first = (this < &to) ? this : &to;
    Account* second = (this < &to) ? &to : this;
    
    lock_guard<mutex> lock1(first->m);
    lock_guard<mutex> lock2(second->m);
    
    this->balance -= amount;
    to.balance += amount;
}

2. std::lock для атомарной блокировки

void transfer(Account& to, int amount) {
    lock(this->m, to.m);  // Блокирует ОДНОВРЕМЕННО
    lock_guard<mutex> l1(this->m, adopt_lock);
    lock_guard<mutex> l2(to.m, adopt_lock);
    
    this->balance -= amount;
    to.balance += amount;
}

3. std::unique_lock с lock()

void transfer(Account& to, int amount) {
    unique_lock<mutex> l1(this->m);
    unique_lock<mutex> l2(to.m, defer_lock);
    
    lock(l1, l2);  // Атомарная блокировка
    
    this->balance -= amount;
    to.balance += amount;
}

Условия для DeadLock (Все 4 необходимы)

  1. Mutual Exclusion — ресурс для одного потока
  2. Hold and Wait — поток держит ресурс и ждёт другого
  3. No Preemption — нельзя отобрать ресурс
  4. Circular Wait — циклическая цепь ожидания

Признаки DeadLock

  • Приложение зависает
  • CPU не растёт
  • Потоки в состоянии "wait"
  • Нет ошибок, молча зависает