阅读
Anthony Williams的“ C ++并发性”(第1版)
您可以找到线程安全堆栈的此实现:
struct empty_stack: std::exception
{
const char* what() const throw()
{
return "empty stack";
}
};
template<typename T>
class threadsafe_stack
{
private:
std::stack<T> data;
mutable std::mutex m;
public:
threadsafe_stack(){}
threadsafe_stack(const threadsafe_stack& other)
{
std::lock_guard<std::mutex> lock(other.m);
data=other.data;
}
threadsafe_stack& operator=(const threadsafe_stack&) = delete;
void push(T new_value)
{
std::lock_guard<std::mutex> lock(m);
data.push(new_value);
}
std::shared_ptr<T> pop()
{
std::lock_guard<std::mutex> lock(m);
if(data.empty()) throw empty_stack();
std::shared_ptr<T> const res(std::make_shared<T>(data.top()));
data.pop();
return res;
}
void pop(T& value)
{
std::lock_guard<std::mutex> lock(m);
if(data.empty()) throw empty_stack();
value=data.top();
data.pop();
}
bool empty() const
{
std::lock_guard<std::mutex> lock(m);
return data.empty();
}
};
因此,在讨论可能的实现时,作者提出了pop()的下一个版本:
void pop(T& value)
{
std::lock_guard<std::mutex> lock(m);
if(data.empty()) throw empty_stack();
value=data.top();
data.pop();
}
并强调了一些警告,这些警告之一是该解决方案要求存储在堆栈中的类型必须是可分配的。
同时,他显示了下一个用法示例:
std::vector<int> result;
some_stack.pop(result);
现在我反对:
我认为还需要注意以下几点:
存储在线程安全堆栈中的类型的赋值运算符不得像std :: vector的赋值运算符那样抛出异常。
我的反对基于?
我反对的原因:
它来自作者提出的下一个解决方案(未实现)的分析:
“如果按值返回不能引发异常,则可以编写一个返回值的pop(),因此存储在线程安全堆栈中的类型需要非抛出复制构造函数或move构造函数” 。
谢谢您的时间。
答案 0 :(得分:1)
为什么您觉得可以抛出empty_stack
却不能抛出向量分配可能引发的一切?
T pop()
的问题在于,它需要首先从堆栈中删除该值,然后再将其复制-可能会抛出该值,此时您会丢失该值;它既不返回也不留在容器中。换句话说,这样的设计不能提供strong exception guarantee。
这就是std::stack
提供两个单独的调用的原因-T top()
在不修改堆栈的情况下读取顶部(如果抛出该堆栈,则堆栈保持不变),而void pop()
则修改该堆栈堆栈,并保证不会抛出。