为什么作者声称这段代码会导致种族
Why does the author claim that this code leads to race?
为什么作者认为下面的部分源代码会导致种族?
作者说:
如果有多个线程从队列中删除项目,则此设计受调用 empty、front 和 pop 之间的争用条件的约束,但在单使用者系统中(如此处讨论),这不是问题。
这是代码:
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();
}
};
如果你调用empty
,你会检查弹出一个元素是否安全。在线程系统中可能发生的情况是,在您检查队列不为空后,另一个线程可能已经弹出了最后一个元素,并且队列不为空不再安全。
thread A: thread B:
if(!queue.empty());
if(!queue.empty());
queue.pop();
->it is no longer sure that the queue
isn't empty
线程"消耗"队列中的数据,则可能会导致争用条件特别糟糕。 采用以下伪代码:
class consumer
{
void do_work()
{
if(!work_.empty())
{
type& t = work_.front();
work_.pop();
// do some work with t
t...
}
}
concurrent_queue<type> work_;
};
这看起来很简单,但是如果您有多个consumer
对象,并且concurrent_queue
中只有一个项目怎么办。 如果使用者在调用empty()
之后但在调用pop()
之前被打断,那么潜在的多个consumer
将尝试处理同一个对象。
更合适的实现将在界面中公开的单个操作中执行空检查和弹出,如下所示:
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 pop(Data& popped)
{
boost::mutex::scoped_lock lock(the_mutex);
if(!the_queue.empty())
{
popped = the_queue.front();
the_queue.pop();
return true;
}
return false;
}
};
因为你可以这样做...
if (!your_concurrent_queue.empty())
your_concurrent_queue.pop();
。并且如果另一个线程调用pop
"介于"这两行之间,pop
仍然失败。
(这在实践中是否真的会发生,取决于并发线程的执行时间 - 本质上线程"竞赛"和谁赢得这场比赛决定了错误是否会表现出来,这在现代抢占式操作系统上本质上是随机的。这种随机性会使竞争条件非常难以诊断和修复。
每当客户端执行这样的"元操作"(其中有一系列多个调用实现预期效果)时,仅通过方法内锁定就不可能防止争用条件。
由于客户端无论如何都必须执行自己的锁定,因此出于性能原因,您甚至可以考虑放弃方法内锁定。只要确保这被清楚地记录下来,这样客户端就知道你没有对线程安全做出任何承诺。
我认为让您感到困惑的是,在您发布的代码中,没有任何内容会导致竞争条件。争用条件将由实际调用此代码的线程引起。假设线程 1 检查线程是否不为空。然后这条线就睡了一年。一年后,当它醒来时,该线程假设队列仍然为空是否仍然有效?好吧,不,与此同时,另一个线程可以很容易地出现并称为推送。
- 在java中解决这段代码时面临循环中的问题
- 修改 VS Code 中的默认C++代码段
- 我是如何在这段代码中出现分段错误的
- 我不明白这段代码是如何对这个pythonlist()进行排序的,也不明白如何用C++中的向量来重现它
- 为什么这段代码不起作用,我该如何解决?
- 为什么这段代码给我错误? 有没有自错?
- 有人可以解释一下这段代码如何能够反转字符串
- 可能我知道为什么这段代码没有给出任何输出吗?
- 这段代码的最后一行在做什么?
- 我不知道为什么这段代码会让核心被转储?
- 我试图用这段代码找到二叉树的高度,但它一直返回 0,有人可以告诉我为什么吗?
- 有人可以向我解释一下这段代码的作用吗?
- 存储在哪个内存段(代码/数据段)类(员工)中?
- 为什么这段代码会导致无限循环?
- 为什么代码段会引发浮点异常?
- 任何人都可以弄清楚这段代码如何显示运行错误?它打印无限时间 -1 以及正确答案
- 如何为一段代码启用 -permissive
- 如何知道用于实现标准代码段的确切数据结构和算法,例如在C++STL中?
- 为什么下面的代码段返回指针指向的值而不是指针的地址?
- 谁能告诉我为什么这段代码没有产生正确的输出?