поведение std :: scoped_lock с одним мьютексом - PullRequest
3 голосов
/ 29 января 2020

Контекст:

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

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

Меня интересует случай, когда у нас есть только один один мьютекс, и поэтому нам не нужно заботиться о предотвращении тупиков.

Я прочитал этот ответ , что:

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

Вопрос:

Мне интересно, насколько верно это предложение.

Я имею в виду, гарантировано ли (по стандарту), что с одним мьютексом std::scoped_lock будет специализированным, чтобы избавиться от ненужного накладные расходы из-за обработки предотвращения тупиков?


Мои мысли:

После некоторого исследования по этому вопросу я обнаружил из cppreference следующее предложение:

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

Что позволило бы нам сделать вывод, что такого не произойдет в противном случае (т. е. если задан только один мьютекс). Но опять же, это всего лишь предположение.

Из этого c ++ черновика Я не вижу явного упоминания о такой специализации. Единственное предложение, которое я получил:

Когда sizeof...(MutexTypes) равно 1, поставляемый тип Mutex должен соответствовать требованиям Cpp17BasicLockable . В противном случае каждый из типов мьютекса должен удовлетворять требованиям Cpp17Lockable .

(выделено мной)

Я знаю, что * 1071 Требования * BasicLockable требуют наличия функций lock() и unlock(), которые удовлетворяют условиям, таким как определенные здесь . С другой стороны, требования Lockable предполагают требования BasicLockable с добавлением функции try_lock(), которая удовлетворяет условиям, таким как определенные там .

Я знаю, что функция try_lock() необходима для запуска алгоритма предотвращения тупиковой ситуации, используемого std::lock.

Из того, что указано в приведенном выше черновом извлечении, функция try_lock(), таким образом, не требуется, если мы даем только один мьютекс std::scoped_lock. Достаточно ли этого, чтобы вывести / учесть, что указанная выше специализация всегда определяется (и предположительно ведет себя так, как std::lock_guard)? Я бы сказал да, но так как я никогда не видел явного упоминания об этом, мне интересно, прав ли я или что-то пропустил?


РЕДАКТИРОВАТЬ:

Я только что заметил, что пропустил самую важную часть здесь , которая гласит:

Эффекты : Инициализирует pm с tie(m...). Тогда, если sizeof...(MutexTypes) равно 0, никаких эффектов нет. В противном случае, если sizeof...(MutexTypes) равно 1, то m.lock(). В противном случае, lock(m...).

(выделено мной)

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

Ответы [ 3 ]

3 голосов
/ 29 января 2020

std::scoped_lock должен вести себя идентично std::lock_guard, если предоставляется только один мьютекс. Отсюда и другое требование для случая с одним мьютексом.

Это может быть сделано со специализацией или с помощью другого внутреннего механизма, если поведение одинаково.

2 голосов
/ 29 января 2020

Если вы читаете спецификацию lock_guard (которая находится прямо над scoped_lock), это должно быть ясно.

[thread.lock.guard] -3

Инициализирует pm с помощью m. Вызывает m.lock ()

[thread.lock.scoped] -3

Инициализирует pm с t ie (m .. .). [...] В противном случае, если sizeof ... (MutexTypes) равен 1, тогда m.lock (). [...]

В нем явно не упоминается использование lock_guard, но требуется такое же поведение.

0 голосов
/ 29 января 2020

Стандарт редко гарантирует некоторую оптимизацию, хотя специализация (Примечательными примерами являются специализации на разных типах итераторов и мерзость, равная std::vector<bool>). Для этого есть два способа go:

  1. Доверьтесь реализации компилятора / стандартной библиотеки. Компиляторы epi c, они выполняют чрезвычайно продвинутые виды оптимизации, о некоторых из которых вы можете только мечтать. Реализации STL в большинстве случаев просто фантастичны c. Есть области, где они работают медленнее, поскольку они должны иметь возможность обрабатывать странные крайние случаи, но здесь уже должна быть другая специализация, поскольку для случая с одним аргументом требуется только BasicLockable, поэтому он будет иметь реализацию, которая не нуждается в try_lock, так почему бы не быть эффективным.
  2. Выполните ваш код. Проверьте, достаточно ли это быстро, проверьте, находится ли scoped_lock на горячем пути вашего кода, и если вы действительно думаете (и у вас есть данные, чтобы доказать это), что scoped_lock медленный, тогда и только тогда замените его на lock_guard и проверьте снова.
...