C ++ 17 представил новый класс блокировки под названием std :: scoped_lock.

Судя по документации, он похож на уже существующий класс std :: lock_guard.

В чем разница и когда его использовать?

Ответы (4)

Единственное и важное отличие состоит в том, что std :: scoped_lock имеет вариативный конструктор, принимающий более одного мьютекса. Это позволяет заблокировать несколько мьютексов во избежание тупика, как если бы использовалось std :: lock.

{
    // safely locked as if using std::lock
    std::scoped_lock lock(mutex1, mutex2);     
}

Раньше вам приходилось танцевать, чтобы безопасно заблокировать несколько мьютексов, используя std :: lock, как объяснялось этот ответ.

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

GCC 7 уже поддерживает std :: scoped_lock, который можно увидеть здесь.

Для получения дополнительной информации вы можете прочитать стандартную бумагу

Поздний ответ, в основном в ответ на:

Вы можете считать std :: lock_guard устаревшим.

В общем случае, когда нужно заблокировать ровно один мьютекс, std :: lock_guard имеет API, который немного безопаснее использовать, чем scoped_lock.

Например:

{
   std::scoped_lock lock;  // protect this block
   ...
}

Приведенный выше фрагмент, вероятно, является случайной ошибкой во время выполнения, потому что он компилируется, а затем абсолютно ничего не делает. Кодер, вероятно, имел в виду:

{
   std::scoped_lock lock{mut};  // protect this block
   ...
}

Теперь блокирует / разблокирует mut.

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

Итак, мой совет - использовать самый простой инструмент для работы:

  1. lock_guard, если вам нужно заблокировать ровно 1 мьютекс для всей области.

  2. scoped_lock, если вам нужно заблокировать количество мьютексов, не равное точно 1.

  3. unique_lock, если вам нужно разблокировать в рамках блока (что включает использование с condition_variable).

Этот совет делает не подразумевает, что scoped_lock следует изменить так, чтобы он не принимал 0 мьютексов. Существуют допустимые варианты использования, в которых желательно, чтобы scoped_lock принимал пакеты параметров вариативного шаблона, которые могут быть пустыми. И пустой регистр должен не ничего блокировать.

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

scoped_lock - это строго улучшенная версия lock_guard, которая блокирует произвольное количество мьютексов одновременно (с использованием того же алгоритма предотвращения взаимоблокировок, что и std :: lock). В новом коде вы должны использовать только scoped_lock.

Единственная причина, по которой lock_guard все еще существует, - это совместимость. Его нельзя было просто удалить, потому что он используется в текущем коде. Более того, оказалось нежелательным изменять его определение (с унарного на вариативное), потому что это тоже наблюдаемое и, следовательно, ломающееся изменение (но по некоторым техническим причинам).

Вот пример и цитата из Параллелизм C ++ в действии:

friend void swap(X& lhs, X& rhs)
{
    if (&lhs == & rhs)
        return;
    std::lock(lhs.m, rhs.m);
    std::lock_guard lock_a(lhs.m, std::adopt_lock);
    std::lock_guard lock_b(rhs.m, std::adopt_lock);
    swap(lhs.some_detail, rhs.some_detail);
}

по сравнению с

friend void swap(X& lhs, X& rhs)
{
    if (&lhs == &rhs)
        return;
    std::scoped_lock guard(lhs.m, rhs.m);
    swap(lhs.some_detail, rhs.some_detail);
}

Существование std :: scoped_lock означает, что большинство случаев, когда вы использовали бы std :: lock до C ++ 17, теперь можно записать с использованием std :: scoped_lock, с меньшим потенциалом ошибок, что может быть только хорошо!

2022 WebDevInsider