std :: scoped_lock или std :: unique_lock или std :: lock_guard? - PullRequest
1 голос
/ 18 октября 2019

Из этого вопроса я понимаю, что std::scoped_lock - это "строго улучшенная версия std::lock_guard".

Из этого вопроса я понимаю, что «std::lock_guard и std::unique_lock одинаковы» за исключением того, что std::unique_lock имеет некоторые дополнительные функции (например, try_lock) за счет некоторых дополнительных затрат.

Как std::scoped_lock сравнивается с std::unique_lock?

Некоторые связанные вопросы, на которые я надеюсь получить ответэтот вопрос.

  1. В чем разница между std::scoped_lock и std::unique_lock?
  2. В каких ситуациях следует использовать std::scoped_lock вместо std::unique_lock?
  3. В каких ситуациях следует использовать std::unique_lock вместо std::scoped_lock?
  4. Почему std::scoped_lock не реализует некоторые дополнительные функции std::unique_lock?

1 Ответ

4 голосов
/ 18 октября 2019

Два объекта предназначены для разных целей. scoped_lock - для простого случая, когда нужно заблокировать некоторое количество объектов мьютекса без тупиков. Блокировка одного мьютекса - это особый случай блокировки нескольких. Объект полностью неподвижен и очень прост.

unique_lock предоставляет ряд функций, некоторые из которых особенно применимы при одновременной блокировке нескольких мьютексов.

  • Отсроченная блокировка. Откладывание должно быть все или ничего;вы либо откладываете блокировку всех мьютексов, либо ни одного из них. Непонятно, почему вы хотите отложить блокировку ряда мьютексов, поскольку вам придется отказаться от любых блокировок, которые были успешными, если какая-либо из них не удалась.

  • Блокировка по таймауту. Если вам нужно время ожидания 100 мс, значит ли это, что блокировка всех мьютексов должна занять не более 100 мс? То есть, если первые 3 блокируются немедленно, но следующий занимает 75 мс, следует ли считать тайм-аут, если пятый занимает 30 мс?

  • Принятие мьютексов. Весь смысл блокировки нескольких мьютексов за одну операцию состоит в том, чтобы избежать тупиков. Это делается путем блокировки взаимных исключений в порядке, который глобально согласован. То есть любое место, где вы блокируете эти объекты мьютекса с помощью std::lock эквивалентных вызовов, будет блокировать их в том же порядке, независимо от того, что.

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

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

Обратите внимание, что std::lock (основа функциональности scoped_lock) даже не пытаетсяобеспечить любую из этих функций.

Может ли существовать специализация scoped_lock, которая использует только один тип мьютекса, который предлагает поведение unique_lock? Конечно. Но это нарушило бы цель scoped_lock, которая должна была быть надежной блокировкой для блокировки взаимных мьютексов. Он lock_guard устарел только случайно, поскольку у него был идентичный интерфейс в случае одного мьютекса.

Кроме того, наличие специализаций по шаблонам с совершенно разными интерфейсами и возможностями обычно не срабатывает. См. vector<bool> в качестве примера.

...