Перейти к содержимому
шпаргалка.
Esc
навигацияоткрыть⌘Jпредпросмотр
На этой странице

Deadlock и состояние гонки

Все темы C++ Developer

Что такое deadlock?

Deadlock (взаимная блокировка) — это состояние, при котором два или более потоков навсегда заблокированы, ожидая ресурсы, захваченные друг другом. Это приводит к остановке выполнения программы.

Пример:

  1. Поток A захватывает ресурс 1 и ждёт ресурс 2.

  2. Поток B захватывает ресурс 2 и ждёт ресурс 1.

  3. Оба потока остаются заблокированными, ожидая освобождения ресурсов.

Причины:

  • Неправильный порядок захвата ресурсов.

  • Нарушение правил синхронизации.

Предотвращение:

  • Использование таймаутов при захвате мьютексов.

  • Соблюдение единого порядка захвата ресурсов.

  • Применение высокоуровневых средств синхронизации (например, std::lock для атомарного захвата нескольких мьютексов).

Пример:

std::mutex mtx1, mtx2;

void threadA() {
    std::lock_guard<std::mutex> lock1(mtx1);
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
    std::lock_guard<std::mutex> lock2(mtx2);
}

void threadB() {
    std::lock_guard<std::mutex> lock2(mtx2);
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
    std::lock_guard<std::mutex> lock1(mtx1);
}

std::thread t1(threadA);
std::thread t2(threadB);
t1.join();
t2.join();

Как избежать состояния гонки?

Чтобы избежать состояния гонки (race condition) в многопоточном программировании, необходимо правильно синхронизировать доступ к общим ресурсам. Вот несколько методов:

  1. Использование мьютексов (std::mutex):

    • Защищает критические секции кода, обеспечивая доступ к ним только одному потоку за раз.
    std::mutex mtx;
    void threadFunc() {
        std::lock_guard<std::mutex> lock(mtx);
        // Доступ к общим данным
    }
  2. Использование атомарных операций (std::atomic):

    • Обеспечивает атомарные обращения к отдельному объекту. Несколько операций над ним или согласование нескольких переменных требуют отдельного анализа. Реализация не всегда lock-free.
    std::atomic<int> counter(0);
    void increment() {
        counter++;
    }
  3. Использование условных переменных (std::condition_variable):

    • Синхронизирует потоки, ожидая выполнения определённых условий.
    std::mutex mtx;
    std::condition_variable cv;
    bool ready = false;
    void producer() {
        std::lock_guard<std::mutex> lock(mtx);
        ready = true;
        cv.notify_one();
    }
    void consumer() {
        std::unique_lock<std::mutex> lock(mtx);
        cv.wait(lock, []{ return ready; });
        // Доступ к общим данным
    }
  4. Соблюдение порядка захвата ресурсов:

    • Захват ресурсов всегда в одном и том же порядке для всех потоков.
    std::mutex mtx1, mtx2;
    void threadFunc() {
        std::lock(mtx1, mtx2);
        std::lock_guard<std::mutex> lock1(mtx1, std::adopt_lock);
        std::lock_guard<std::mutex> lock2(mtx2, std::adopt_lock);
        // Доступ к общим данным
    }

Эта страница была полезной?