Почему lock_guard не уничтожается после вызова другой функции?

В моей кодовой базе есть две функции, которые выглядят примерно так:

void inside() {
  lock_guard<mutex> lock(LOCK);
  cout << "Finished inside" << endl;
}

void outside() {
  lock_guard<mutex> lock(LOCK);
  inside();
  cout << "Finished outside" << endl;
}

Это вызывает взаимоблокировку в моей кодовой базе, что я нахожу странным, потому что у меня сложилось впечатление, что lock_guard уничтожается, когда выходит за пределы области видимости. Я также пробовал с unique_lock, но получил тот же результат. Единственный способ, которым я смог решить эту проблему, - вызвать разблокировку перед вызовом внутрь:

void outside() {
  LOCK.lock();
  // Do stuff
  LOCK.unlock();
  inside();
  cout << "Finished outside" << endl;
}

Ну что, вышло за рамки?

user253751 18.12.2020 23:28

На мой взгляд, это выходит за рамки, если я не ошибаюсь

doctopus 18.12.2020 23:29

Он не выходит за рамки до тех пор, пока функция, в которой он определен, не завершится. В полезном упрощении область определяется ближайшими фигурными скобками {}, в которых определена переменная - в данном случае {} функции. Пока выполнение кода не завершит эту область, она находится в области.

xaxxon 18.12.2020 23:31

Это объясняет, спасибо

doctopus 18.12.2020 23:31
en.cppreference.com/w/cpp/language/scope
xaxxon 18.12.2020 23:38
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
5
736
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

У меня сложилось впечатление, что lock_guard уничтожается, когда выходит за рамки.

Оно делает. Проблема в том, что вы создаете 2 отдельных объекта lock_guard в 2 разных областях. В outside() вы создаете один объект, который остается в области видимости, пока inside() работает. inside() затем создает свой собственный объект, который выходит за рамки, когда inside() выходит. Объект в outside() остается в области до тех пор, пока outside() не выйдет.

При этом обратите внимание, что std::mutex не является повторным входом. Попытка заблокировать mutex, когда блокировка уже принадлежит вызывающему потоку, является поведением undefined. У вас есть 2 lock_guard объекта, пытающихся заблокировать один и тот же mutex из одного потока. Вот почему вы зашли в тупик. Вам нужно будет использовать std::recursive_mutex вместо этого, чтобы избежать этого. Поток может владеть несколькими блокировками для recursive_mutex, если он правильно разблокирует их все.

В противном случае, если вы действительно хотите, чтобы outside() разблокировал mutex перед входом в inside(), более чистый (и безопасный) способ справиться с этим — ввести новую область видимости в outside(), например:

void outside() {
  { // <--- starts a new scope here
  lock_guard<mutex> lock(LOCK);
  // Do stuff
  } // <-- ends the scope, destroying the lock_guard here
  inside();
  cout << "Finished outside" << endl;
}

Итак, мораль этой истории заключается в использовании recursive_mutex, если мьютекс используется несколькими потоками?

doctopus 18.12.2020 23:46

@doctopus • Нет, recursive_mutex, если мьютекс будет заблокирован в разных областях/цепочках вызовов в одном потоке.

Eljay 18.12.2020 23:50

Я понимаю. Спасибо

doctopus 19.12.2020 00:06

Другие вопросы по теме