Механизм атомарного залочивания двух mutex

Атомарное залочивание двух (или более) мьютексов — это сложная задача, поскольку неправильная реализация может привести к взаимоблокировке (deadlock). Взаимоблокировка возникает, когда два потока ждут освобождения мьютексов, которые друг у друга заняты. Для решения этой проблемы используется несколько методов. Один из них — использование стандартной функции `std::lock` из C++11, которая позволяет атомарно залочить несколько мьютексов.

std::lock

Гарантирует, что все переданные ей мьютексы будут залочены без возможности возникновения взаимоблокировки. Если один из мьютексов не может быть залочен, функция разблокирует уже захваченные мьютексы и начнет попытку захвата заново.

Пример кода:

```cpp
#include <iostream>
#include <thread>
#include <mutex>

std::mutex m1, m2;

void threadFunction1() {
    std::lock(m1, m2); // атомарное залочивание
    std::lock_guard<std::mutex> lg1(m1, std::adopt_lock);
    std::lock_guard<std::mutex> lg2(m2, std::adopt_lock);
    // Критическая секция
    std::cout << "Thread 1 has locked both mutexes" << std::endl;
    // мьютексы автоматически разблокируются при выходе из области видимости
}

void threadFunction2() {
    std::lock(m1, m2); // атомарное залочивание
    std::lock_guard<std::mutex> lg1(m1, std::adopt_lock);
    std::lock_guard<std::mutex> lg2(m2, std::adopt_lock);
    // Критическая секция
    std::cout << "Thread 2 has locked both mutexes" << std::endl;
    // мьютексы автоматически разблокируются при выходе из области видимости
}

int main() {
    std::thread t1(threadFunction1);
    std::thread t2(threadFunction2);

    t1.join();
    t2.join();

    return 0;
}
```

Объяснение кода:

1. std::lock(m1, m2): Эта функция пытается залочить оба мьютекса одновременно. Если это не удается (например, если один мьютекс уже захвачен другим потоком), то освобождаются все захваченные мьютексы и начинается новая попытка захвата.

2. std::lock_guard<std::mutex> lg1(m1, std::adopt_lock): Эта конструкция создает объект `std::lock_guard`, который автоматически разблокирует мьютекс при выходе из области видимости. Параметр `std::adopt_lock` указывает, что мьютекс уже захвачен и не должен быть захвачен повторно.

3. Критическая секция: Между захватом и освобождением мьютексов располагается код, который должен выполняться безопасно в многопоточной среде.

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

May 24, 2024, easyoffer