我创建了一个线程安全队列(请参阅代码)。这个类似乎可以工作,但现在我想让front()加pop()的组合线程安全,这样线程首先获取元素,然后确定删除相同的元素。我可以想出一些解决方案,但它们对用户来说并不优雅,否则将失去强大的异常安全保障。
第一个解决方案是,用户只需锁定ThreadQueueu,而不是调用front()和pop()并解锁ThreadQueue。然而,该类的整体思想是,用户不必关心线程安全性。
第二种解决方案是将队列锁定在重载函数front()中,只在pop()中将其解锁。然而,在这种情况下,不允许用户只调用front()或pop(),而不是用户友好的。。
我想到的第三个选项是在类(frontPop)中创建一个公共函数,该函数返回front元素并将其删除。然而,在这种情况下,异常安全性消失了。
什么是既友好(优雅)又能维护异常安全的解决方案?
class ThreadQueue: private std::queue<std::string>
{
mutable std::mutex d_mutex;
public:
void pop()
{
lock_guard<mutex> lock(d_mutex);
pop();
}
std::string &front()
{
lock_guard<mutex> lock(d_mutex);
return front();
}
// All other functions
private:
};
通常的解决方案是提供一个组合的前端&pop,接受用于存储弹出值的引用,并在弹出值时返回bool
,即true
:
bool pop(std::string& t) {
lock_guard<mutex> lock(d_mutex);
if (std::queue<std::string>::empty()) {
return false;
}
t = std::move(std::queue<std::string>::front());
std::queue<std::string>::pop();
return true;
}
移动赋值引发的任何异常都会在修改队列之前发生,从而保持值类型的移动赋值运算符提供的异常保证。