gpt4 book ai didi

c++ - 为什么 get_tail() 应该使用 tail_mutex 上的锁?

转载 作者:行者123 更新时间:2023-11-28 06:00:39 25 4
gpt4 key购买 nike

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 call try_pop() and a thread to call push() 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 to push(), 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 the tail_mutex in get_tail() solves everything. Because the call to get_tail() locks the same mutex as the call to push(), there’s a defined order between the two calls. Either the call to get_tail() occurs before the call to push(), in which case it sees the old value of tail, or it occurs after the call to push(), 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/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com