我目前正在尝试编写一个并发队列,但我有一些我无法向自己解释的段错误。我的队列实现基本上由该站点上的第一个列表给出。
网站说如果对象从队列中并行删除就会出现竞争情况,但我只是不明白为什么会有这样的情况,有人可以向我解释一下吗?
编辑:这是代码:
template<typename Data>
class concurrent_queue
{
private:
std::queue<Data> the_queue;
mutable boost::mutex the_mutex;
public:
void push(const Data& data)
{
boost::mutex::scoped_lock lock(the_mutex);
the_queue.push(data);
}
bool empty() const
{
boost::mutex::scoped_lock lock(the_mutex);
return the_queue.empty();
}
Data& front()
{
boost::mutex::scoped_lock lock(the_mutex);
return the_queue.front();
}
Data const& front() const
{
boost::mutex::scoped_lock lock(the_mutex);
return the_queue.front();
}
void pop()
{
boost::mutex::scoped_lock lock(the_mutex);
the_queue.pop();
}
};
答案 0 :(得分:1)
如果您尝试从中读取项目时队列为空,该怎么办?
想想这个用户代码:
while(!q.empty()) //here you check q is not empty
{
//since q is not empty, you enter inside the loop
//BUT before executing the next statement in this loop body,
//the OS transfers the control to the other thread
//which removes items from q, making it empty!!
//then this thread executes the following statement!
auto item = q.front(); //what would it do (given q is empty?)
}
答案 1 :(得分:0)
如果您使用空并且发现队列不为空,则在您使用结果之前,另一个线程可能会弹出该项,使其变空。
同样对于前面,您可以阅读前面的项目,并且在您使用该项目时它可以被另一个线程弹出。
答案 2 :(得分:0)
@ parkydr和@Nawaz的回答是正确的,但这是另一种思考的食物;
你想要实现什么目标?
有时候(我不敢说经常)错误地拥有一个线程安全队列的原因。在许多情况下,您希望在队列只是实现细节的上下文中“锁定”队列“。”
一个原因是,线程安全队列用于消费者 - 生产者情况,其中 1-N 节点推送数据, 1-M 节点从中弹出,无论它们得到什么。队列中的所有元素都被视为相等,并且消费者只是在不知道他们得到什么的情况下弹出,并开始处理数据。在这种情况下,您的界面不应公开T& front()
。好吧,如果你不确定那里有一个项目,你永远不应该返回一个参考(在并行情况下,如果没有外部锁定,你永远不能确定。)
我建议使用unique_ptr
(或当然是shared_ptr
)并且只展示无竞赛功能(为简洁起见我省略了const功能)。使用std::unique_ptr
将需要C ++ 11,但如果您无法使用C ++ 11,则可以使用boost::shared_ptr
获得相同的功能:
// Returns the first item, or an empty unique_ptr
std::unique_ptr< T > pop( );
// Returns the first item if it exists. Otherwise, waits at most <timeout> for
// a value to be be pushed. Returns an empty unique_ptr if timeout was reached.
std::unique_ptr< T > pop( {implementation-specific-type} timeout );
void push( std::unique_ptr< T >&& ptr );
exist()
和front()
等功能自然会成为竞争条件的受害者,因为它们无法自动执行您(您认为)想要的任务。 exist()
有时会在收到结果时返回一个不正确的值,如果队列为空,则front()
必须抛出。
答案 3 :(得分:0)
我认为empty()
功能无用/危险的原因很清楚。如果您想要阻止队列,请删除它。
相反,添加一个条件变量(boost :: condition,IIRC)。 push / pop的功能如下所示:
void push(T data)
{
scoped_lock lock(mutex);
queue.push(data);
condition_var.notify_one();
}
data pop()
{
scoped_lock lock(mutex);
while(queue.empty())
condition_var.wait(lock);
return queue.pop();
}
请注意,这是伪代码,但我相信你可以解决这个问题。也就是说,建议使用unique_ptr(或C98的auto_ptr)来避免复制实际数据是一个好主意,但这是一个完全独立的问题。