首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >为什么get_tail()要使用tail_mutex上的锁?

为什么get_tail()要使用tail_mutex上的锁?
EN

Stack Overflow用户
提问于 2015-10-26 08:37:00
回答 1查看 85关注 0票数 0
代码语言:javascript
复制
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锁的尾部。

书上说:

事实证明,tail_mutex上的锁不仅是保护尾本身的读取所必需的,而且还必须确保不会有数据竞争从头读取数据。如果您没有这个互斥对象,那么线程很可能调用try_pop(),线程可以并发调用push(),并且它们的操作没有定义的顺序。即使每个成员函数都对互斥锁持有锁,但它们对不同互斥锁持有锁,并且它们可能访问相同的数据;毕竟,队列中的所有数据都来自对push()的调用。因为线程可能访问相同的数据而没有定义的顺序,这将是一个数据竞争和未定义的行为。谢天谢地,tail_mutexget_tail()中的锁解决了所有问题。因为对get_tail()的调用与对push()的调用锁定相同的互斥对象,所以这两个调用之间有一个定义的顺序。对get_tail()的调用发生在对push()的调用之前,在这种情况下,它看到尾的旧值,或者在调用push()之后发生,在这种情况下,它会看到尾的新值和附加到以前的尾值的新数据。

我不太明白这一点:如果我只使用head.get() == tail,这种比较要么发生在tail = new_tail in push()中,将head.get()与旧的tail值进行比较,要么在将head.get()tail的新值进行比较之后,为什么会出现数据竞争?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2015-10-26 13:39:09

我不同意这点。get_tail不应该有任何互斥,这个函数本身不容易发生数据竞争,也不容易发生内存重排。事实上,应该完全消除get_tail。用户的尾巴应该保护使用作为学徒,但把互斥进入获取尾巴实际上是一个可怕的反模式。当然,在每个函数中添加互斥对象将使您的程序线程安全。它还将使它有效地单线程-如果单线程是需要的,只需不使用线程。

多线程的艺术并不在于把互斥放到任何地方。它是在不使用它们。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/33341492

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档