Назад к вопросам
Middle+
103
questionbank

Как работает механизм атомарного залочивания двух мьютексов?

Sobes Copilot

Получайте ответы в реальном времени

Ответ от нейросети

sobes.tech AI

Для корректной атомарной блокировки двух мьютексов используется функция std::lock. Она гарантирует, что либо оба мьютекса будут успешно заблокированы, либо ни один из них.

Алгоритм std::lock следующий:

  1. Пытается заблокировать первый мьютекс.
  2. Если успешно, пытается заблокировать второй мьютекс.
  3. Если второй мьютекс также успешно заблокирован, оба мьютекса остаются заблокированными.
  4. Если заблокировать второй мьютекс не удалось (например, он уже заблокирован другим потоком), то блокировка с первого мьютекса снимается, и функция ждет, пока оба мьютекса станут свободными, затем повторяет попытку.

Это предотвращает взаимные блокировки (deadlock), которые могли бы возникнуть при последовательной блокировке вручную:

// Пример ошибки:
std::mutex m1, m2;

void func1() {
    m1.lock();
    m2.lock(); // Может вызвать deadlock, если другой поток уже заблокировал m2 и ждет m1
    // ... работа с защищенными ресурсами
    m2.unlock();
    m1.unlock();
}

void func2() {
    m2.lock();
    m1.lock(); // Может вызвать deadlock
    // ... работа с защищенными ресурсами
    m1.unlock();
    m2.unlock();
}

С использованием std::lock:

#include <mutex>

std::mutex m1, m2;

void safe_func() {
    std::lock(m1, m2); // Атомарная блокировка обоих мьютексов
    // ... работа с защищенными ресурсами
    // Освобождение мьютексов в обратном порядке или с помощью lock_guard
    m2.unlock();
    m1.unlock();
}

Более предпочтительный способ с использованием std::unique_lock и std::adopt_lock:

#include <mutex>
#include <unique_lock>

std::mutex m1, m2;

void safe_func_with_guards() {
    std::lock(m1, m2); // Атомарная блокировка
    std::unique_lock<std::mutex> lock1(m1, std::adopt_lock); // Связываем lock_guard с уже заблокированным мьютексом
    std::unique_lock<std::mutex> lock2(m2, std::adopt_lock); // Связываем lock_guard с уже заблокированным мьютексом
    // Теперь мьютексы будут автоматически разблокированы при выходе из области видимости
    // ... работа с защищенными ресурсами
}

Использование std::lock с std::unique_lock является наилучшей практикой, так как гарантирует правильное освобождение мьютексов даже при исключениях.