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
якщо вам потрібно розблокувати в межах блоку (що включає використання з a 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);
}
vs.
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
, з меншим потенціалом помилок, що може бути лише хорошою справою!