Ответ
В C++ для атомарной блокировки нескольких мьютексов с защитой от взаимоблокировок (deadlock) используются два основных механизма:
-
std::lock(C++11): Алгоритмически безопасно захватывает несколько мьютексов, используя deadlock-avoidance алгоритм (например, алгоритм обедающих философов). После захвата мьютексы передаются RAII-обёрткам с флагомstd::adopt_lock.std::mutex mtx1, mtx2; { std::lock(mtx1, mtx2); // Атомарный захват std::lock_guard<std::mutex> lock1(mtx1, std::adopt_lock); std::lock_guard<std::mutex> lock2(mtx2, std::adopt_lock); // Критическая секция защищена обоими мьютексами } // Мьютексы освобождаются автоматически -
std::scoped_lock(C++17): Более идиоматичная заменаstd::lockс variadic templates. Автоматически применяет deadlock-avoidance и обеспечивает RAII.std::mutex mtx1, mtx2; { std::scoped_lock lock(mtx1, mtx2); // Всё в одной строке // Критическая секция } // Автоматический unlock
Почему это важно? Последовательный захват (mtx1.lock(); mtx2.lock();) в разных потоках в обратном порядке — классическая причина deadlock. std::lock и std::scoped_lock гарантируют, что захват произойдёт атомарно для всей группы, исключая эту проблему.