Я читаю книгу «C++ Concurrency in action» и пытаюсь понять безопасность исключений в потокобезопасных структурах данных (например, стек). Автор утверждает, что, чтобы избежать состояния гонки, pop
должны делать обе операции - выскочить и вернуть элемент из стека, однако:Возврат shared_ptr и безопасности исключений
Если функция поп() была определена, чтобы вернуть значение совал, а также удалить его из стек, у вас есть потенциальная проблема: значение, которое вызывается, возвращается вызывающему только после того, как стек был изменен, но процесс копирования данных для возврата к вызывающему может вызвать исключение.
Здесь предлагается решение:
std::shared_ptr<T> pop()
{
std::lock_guard<std::mutex> lock(m);
if(data.empty()) throw runtime_error("empty");
std::shared_ptr<T> const res(std::make_shared<T>(data.top()));
data.pop();
return res;
}
В этом случае мы получили вызов копии конструктора на make_shared
линии. Если конструктор копирования генерирует исключение, стек еще не изменен, и мы хороши.
Однако я не вижу, как она сильно отличается от этого фрагмента кода:
T pop2() {
std::lock_guard<std::mutex> lock(m);
if(data.empty()) throw runtime_error("empty");
auto res = data.top();
data.pop();
return res;
}
Здесь мы имеем копии конструктора вызова на data.top()
линии и перемещения конструктора по возвращению. Опять же, если конструктор копирования генерирует исключение, мы хороши, так как стек еще не изменен. Перемещение конструктора не должно допускать исключения.
Я что-то упустил? Какая польза от возврата shared_ptr
сравнение с возвратом (подвижным) T
?
Перемещение конструкторов разрешено. – Simple
В моей PDF-копии книги 'throw empty_stack()' вместо 'throw runtime_error (" empty ")'. Класс 'empty_stack' в книге наследуется непосредственно из' std :: exception'. Если 'runtime_error' в вашем вопросе является' std :: runtime_error', тогда обратите внимание, что 'throw runtime_error (« empty »);' [может также использовать другие исключения] (https://stackoverflow.com/questions/36106747/как к конструкции-а-stdexcept-исключения-без метания). – jotik
Да, у меня тоже есть 'empty_stack'. Я просто заменил его «runtime_exception» в примере. – lstipakov