std::lock_guard или std::scoped_lock?

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

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

Какая разница и когда я должен его использовать?

2 ответа

Решение

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

{
    // safely locked as if using std::lock
    std::scoped_lock<std::mutex, std::mutex> 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++ Concurrency in Action:

friend void swap(X& lhs, X& rhs)
{
    if (&lhs == & rhs)
        return;
    std::lock(lhs.m, rhs.m);
    std::lock_guard<std::mutex> lock_a(lhs.m, std::adopt_lock);
    std::lock_guard<std::mutex> 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с меньшим потенциалом для ошибок, которые могут быть только хорошими!

Другие вопросы по тегам