Какие RAII-обертки для управления блокировками (Lock Guards) есть в стандартной библиотеке C++?

«Какие RAII-обертки для управления блокировками (Lock Guards) есть в стандартной библиотеке C++?» — вопрос из категории Многопоточность, который задают на 25% собеседований C/C++ Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Стандартная библиотека C++ предоставляет три основные RAII-обертки для безопасного управления мьютексами.

1. std::lock_guard (C++11) - базовая обертка:

  • Простая и эффективная. При создании сразу захватывает мьютекс, при разрушении - освобождает.
  • Нельзя разблокировать досрочно или передавать владение.
  • Идеален для большинства критических секций.
    std::mutex mtx;
    {
    std::lock_guard<std::mutex> lock(mtx); // Захват здесь
    // Критическая секция защищена
    } // Автоматическое освобождение при выходе из области видимости

2. std::unique_lock (C++11) - гибкая обертка:

  • Предоставляет расширенный контроль. Поддерживает отложенную блокировку, попытки захвата, повторную блокировку и передачу владения.
  • Имеет небольшие накладные расходы по сравнению с lock_guard.
    
    std::mutex mtx;
    std::unique_lock<std::mutex> lock(mtx, std::defer_lock); // Создаем, но не захватываем

// Захватываем позже, возможно, с таймаутом или попыткой if (lock.try_lock_for(std::chrono::milliseconds(100))) { // Работа в критической секции lock.unlock(); // Можно освободить досрочно // ... какая-то не защищенная работа lock.lock(); // И захватить снова } // Все еще автоматически освобождается при разрушении


**3. `std::scoped_lock` (C++17) - для нескольких мьютексов:**
*   **Аналог `lock_guard` для множественной блокировки.**
*   **Гарантирует deadlock-free захват** нескольких мьютексов с помощью алгоритма `std::lock`.
*   Предпочтительнее `std::lock_guard` при работе с более чем одним мьютексом.
```cpp
std::mutex mtx1, mtx2, mtx3;
{
    // Захватывает все три мьютекса атомарно, избегая deadlock.
    std::scoped_lock lock_all(mtx1, mtx2, mtx3);
    // Критическая секция, использующая все три ресурса
} // Все мьютексы освобождаются в обратном порядке
Краткое сравнение: Обертка Множественные мьютексы Отложенный захват Попытка захвата Ручное управление
lock_guard
unique_lock
scoped_lock

Выбор: Используйте scoped_lock для нескольких мьютексов, unique_lock когда нужна гибкость (условные переменные, таймауты), и lock_guard для простых случаев.