Попробуйте поймать блокировки в С++

В Java:

Lock lock = new ReentrantLock();
try{
  lock.lock();
  someFunctionLikelyToCauseAnException();
}
catch(e){...}
finally {
  lock.unlock();
}

Мой вопрос заключается в этом вышеприведенном примере, мы знаем, что блокировка всегда будет разблокирована, потому что, наконец, всегда выполняется, но какова гарантия с помощью C++?

mutex m;
m.lock();
someFunctionLikelyToCauseAnException();
/// ????

Как это будет работать и почему?

Ответ 1

Для этого мы используем конструкцию std::lock_guard стиле std::lock_guard. Когда вы используете

std::mutex m;
{ // start of some scope
    std::lock_guard lg(m);
    // stuff
} // end of scope

lg гарантирует, что m будет разблокирован независимо от того, какой путь остается в области, поскольку он разрушен при выходе области, и деструктор std::lock_guard вызовет unlock

Даже если выбрано исключение, стек будет размотан (разматывание стека), и этот процесс уничтожит lg который, в свою очередь, вызовет unlock гарантирующую освобождение блокировки.

Ответ 2

Какова гарантия с C++?

Соответствующая гарантия в C++ работает немного по-другому по сравнению с той, которую вы упоминаете в Java. Вместо блока finally он полагается на уничтожение автоматических переменных, которые происходят при выходе из области, поскольку фрейм стека разматывается. Эта разворачивание стека происходит независимо от того, как была выведена область действия, будь то изящно или из-за исключения.

Предпочтительным подходом к сценарию, касающимся таких блокировок, является использование RAII, как реализовано, например, std::lock_guard. Он содержит объект mutex переданный его конструктору, внутри которого он вызывает метод mutex lock(), после которого поток владеет мьютексом - и после разворачивания стека при выходе области его деструктор вызывается - внутри которого он вызывает метод unlock() mutex unlock(), освобождая его.

Код будет выглядеть так:

std::mutex m;
{
    std::lock_guard lock(m);
    // Everything here is mutex-protected.
}
// Here you are guaranteed the std::mutex is released.

Ответ 3

Если во время выполнения фрагмента кода, защищенного критическим разделом, генерируется исключение, то есть коды между "lock()" и "unlock()", это означает, что связанный объект, над которым работает часть кода, больше не в правильном состоянии. Это может быть отменено или не отменено путем автоматического раскручивания стека, вызванного исключением, поскольку некоторый побочный эффект может иметь место до того, как будет выбрано исключение (сообщение отправлено через сокет, машина была запущена, например). На данный момент большая проблема здесь не в том, что мьютексы будут выпущены (единственная гарантия от использования lock_guard вместо этого). Вполне возможно, что некоторые случаи, когда мьютекс все еще заблокирован, являются желательным поведением и могут быть явно сброшены после того, как вызывающий абонент очистит все беспорядок.

Я хочу сказать: это не проблема языка. Никакая языковая функция не может гарантировать правильную обработку ошибок. Не используйте lock_guard и RAII в качестве серебряной пули.