- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
template<typename T>
class threadsafe_queue
{
private:
struct node
{
std::shared_ptr<T> data;
std::unique_ptr<node> next;
};
std::mutex head_mutex;
std::unique_ptr<node> head;
std::mutex tail_mutex;
node* tail;
node* get_tail()
{
std::lock_guard<std::mutex> tail_lock(tail_mutex);
return tail;
}
std::unique_ptr<node> pop_head()
{
std::lock_guard<std::mutex> head_lock(head_mutex);
// is it necessary to use get_tail()
if(head.get()==get_tail())
{
return nullptr;
}
std::unique_ptr<node> const old_head=std::move(head);
head=std::move(old_head->next);
return old_head;
}
public:
threadsafe_queue():
head(new node),tail(head.get())
{}
threadsafe_queue(const threadsafe_queue& other)=delete;
threadsafe_queue& operator=(const threadsafe_queue& other)=delete;
std::shared_ptr<T> try_pop()
{
std::unique_ptr<node> old_head=pop_head();
return old_head?old_head->data:std::shared_ptr<T>();
}
void push(T new_value)
{
std::shared_ptr<T> new_data(
std::make_shared<T>(std::move(new_value)));
std::unique_ptr<node> p(new node);
node* const new_tail=p.get();
std::lock_guard<std::mutex> tail_lock(tail_mutex);
tail->data=new_data;
tail->next=std::move(p);
tail=new_tail;
}
};
以上代码摘自第 162 页的“C++ 并发操作”。这里它使用 get_tail()
获取尾部并锁定 tail_mutex
。
书上说:
It turns out that not only is the lock on
tail_mutex
necessary to protect the read of tail itself, but it’s also necessary to ensure that you don’t get a data race reading the data from the head. If you didn’t have that mutex, it would be quite possible for a thread to calltry_pop()
and a thread to callpush()
concurrently, and there’d be no defined ordering on their operations. Even though each member function holds a lock on a mutex, they hold locks on different mutexes, and they potentially access the same data; all data in the queue originates from a call topush()
, after all. Because the threads would be potentially accessing the same data without a defined ordering, this would be a data race and undefined behavior. Thankfully the lock on thetail_mutex
inget_tail()
solves everything. Because the call toget_tail()
locks the same mutex as the call topush()
, there’s a defined order between the two calls. Either the call toget_tail()
occurs before the call topush()
, in which case it sees the old value of tail, or it occurs after the call topush()
, in which case it sees the new value of tail and the new data attached to the previous value of tail.
我不太明白这一点:如果我只是使用 head.get() == tail
,这个比较要么发生在 tail = new_tail
之前 push()
将 head.get()
与 tail
的旧值进行比较,或者在 head.get()
之后进行比较> 使用 tail
的新值,为什么会出现数据竞争?
最佳答案
我不同意这一点。 get_tail
中不应该有任何互斥体,这个函数本身不会发生数据争用,也不会发生内存重新排序。事实上,get_tail
应该完全消除。 tail 的用户应该适本地保护用法,但是将 mutex 放在 get tail 中实际上是一个可怕的反模式。当然,将互斥锁放在每个函数中将使您的程序线程安全。它还将使它成为有效的单线程 - 如果需要单线程,则不要使用线程。
多线程的艺术并不在于到处放置互斥体。它不使用它们。
关于c++ - 为什么 get_tail() 应该使用 tail_mutex 上的锁?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33341492/
template class threadsafe_queue { private: struct node { std::shared_ptr data;
我是一名优秀的程序员,十分优秀!