std :: lock_guard або std :: scoped_lock?


143

C ++ 17 представив новий клас блокування під назвою std::scoped_lock.

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

У чому різниця і коли я повинен її використовувати?

Відповіді:


125

Це scoped_lockсуворо перевершена версія, lock_guardщо блокує довільну кількість мутексів відразу (використовуючи той самий алгоритм уникнення тупикової ситуації, що і std::lock). У новому коді ви повинні використовувати будь-коли scoped_lock.

Єдина причина lock_guardвсе ще існує - сумісність. Його не можна було просто видалити, оскільки він використовується в поточному коді. Більше того, виявилося небажаним змінювати своє визначення (від уніарного до варіатичного), оскільки це також є спостережуваним, а отже, і руйнуючим, зміною (але з певних технічних причин).


8
Крім того, завдяки виведенню аргументів шаблону класу, вам навіть не доведеться перераховувати типи, що блокуються.
Нікол Болас

3
@NicolBolas: Це правда, але це стосується і lock_guard. Але це, безумовно, полегшує заняття охороною.
Керрек СБ

6
scoped_lock - лише C ++ 17
Shital Shah

1
Оскільки це c ++ 17, сумісність є особливо вагомою причиною його існування. Я також жорстоко не згоден з будь-яким абсолютистським твердженням, що "ти повинен коли-небудь використовувати", коли чорнило все ще висихає від цього стандарту.
Пол Чайлдс

88

Єдина і важлива відмінність полягає в тому, що він 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яку можна побачити тут .

Для отримання додаткової інформації ви можете прочитати стандартний папір


9
Відповів на власне запитання лише через 10 хв. Ви справді не знали?
Вальтер


3
Коли я підніс це в комітеті, відповідь була "нічого". Може бути, що вироджений випадок якогось алгоритму - це саме правильна річ. Або може бути так, що достатньо людей випадково нічого не замикають, коли вони мали намір щось заблокувати - це загальна проблема. Я справді не впевнений.
Говард Хінант

3
@HowardHinnant : scoped_lock lk; // locks all mutexes in scope. LGTM.
Керрек СБ

2
@KerrekSB: scoped_lock lk;це нова стенограма для scoped_lock<> lk;. Там немає жодного м'ютексів. Тож ти маєш рацію. ;-)
Говард Хінант

26

Пізня відповідь і в основному у відповідь на:

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

Ця рада зовсім НЕ означає , що scoped_lockповинен бути перероблений , щоб не приймати 0 семафорів. Існують дійсні випадки використання, коли бажано scoped_lockприймати різні параметри параметрів шаблону, які можуть бути порожніми. І порожній корпус нічого не повинен замикати.

І тому lock_guardне застаріло. scoped_lock і unique_lock може бути набором функціональних можливостей lock_guard, але цей факт - меч з двома острими. Іноді так само важливо, що тип не буде робити (конструкція за замовчуванням у цьому випадку).


13

Ось зразок і цитата від 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, з меншим потенціалом помилок, що може бути лише хорошою справою!

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.