C ++ 17 ввел новый класс блокировки под названием std::scoped_lock
.
Судя по документации, он похож на уже существующий std::lock_guard
класс.
Какая разница и когда я должен его использовать?
C ++ 17 ввел новый класс блокировки под названием std::scoped_lock
.
Судя по документации, он похож на уже существующий std::lock_guard
класс.
Какая разница и когда я должен его использовать?
Ответы:
Это scoped_lock
строго улучшенная версия, lock_guard
которая блокирует произвольное количество мьютексов одновременно (используя тот же алгоритм предотвращения тупиков, что и std::lock
). В новом коде вы должны только когда-либо использовать scoped_lock
.
Единственная причина lock_guard
все еще существует для совместимости. Его нельзя просто удалить, поскольку он используется в текущем коде. Более того, оказалось нежелательным менять свое определение (с унарного на вариационное), потому что это также наблюдаемое и, следовательно, разрушительное изменение (но по некоторым техническим причинам).
lock_guard
. Но это, безусловно, делает классы охраны немного проще в использовании.
Единственное и важное отличие состоит в том, что 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
которую можно увидеть здесь .
Для получения дополнительной информации вы можете прочитать стандартную статью
scoped_lock lk; // locks all mutexes in scope
. LGTM.
scoped_lock lk;
это новое сокращение для scoped_lock<> lk;
. Там нет ни одного мьютексов. Значит ты прав. ;-)
Поздний ответ, и в основном в ответ на:
Вы можете считать
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
.
Поэтому мой совет - использовать самый простой инструмент для работы:
lock_guard
если вам нужно заблокировать ровно 1 мьютекс для всей области видимости.
scoped_lock
если вам нужно заблокировать несколько мьютексов, которые не точно 1.
unique_lock
если вам нужно разблокировать в рамках блока (который включает в себя использование с condition_variable
).
Этот совет вовсе не означает , что scoped_lock
должен быть переработан , чтобы не принимать 0 семафоров. Существуют допустимые случаи использования, в которых желательно scoped_lock
принимать пакеты параметров вариабельного шаблона, которые могут быть пустыми. И пустой корпус не должен ничего блокировать.
И именно поэтому lock_guard
не считается устаревшим. scoped_lock
и unique_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
, с меньшим риском ошибок, что может быть только хорошо!