为什么在这个无锁堆栈类中“删除”节点会导致竞争条件?

时间:2016-09-16 18:37:51

标签: c++ c++11 concurrency atomic race-condition

在Anthony Williams的第7.2.1节的题为“C ++ Concurrency in Action”的书中,列出了一个无锁堆栈实现:

template <typename T>
class lock_free_stack {
    struct node {
        shared_ptr<T> data_;
        node* next_;
        node(const T& data) : data_(make_shared(data)) {}
    };
    atomic<node*> head_;
public:
    void push(const T& data)
    {
        node* new_node = new node(data);
        new_node->next_ = head_.load();
        while(!head.compare_exchange_weak(new_node->next_, new_node));
    }
    shared_ptr<T> pop()
    {
        node* old_head = head_.load();
        while (old_head &&
                !head_.compare_exchange_weak(old_head, head_->next_));
        return old_head ? old_head->data_ : shared_ptr<T>();
    }
};

然后在7.2.2节中,作者说“...在pop()中,我们选择泄漏节点以避免竞争条件,其中一个线程删除一个节点,而另一个线程仍然拥有指向它的指针它只是要取消引用。“

1)我不明白为什么会出现这种情况以及为什么以下pop()函数会导致竞争条件:

shared_ptr<T> pop()
{
    node* old_head = head_.load(); // (1)
    while (old_head &&
            !head_.compare_exchange_weak(old_head, head_->next_)); // (2)
    shared_ptr<T> res; // (3)
    if (old_head) {
        res.swap(old_head->data);
        delete old_head;
        return res;
    } else {
        return {};
    }
}

2)对于同时调用pop()的多个线程,'old_head'变量可以指向第(3)行之后的同一个节点对象?

2 个答案:

答案 0 :(得分:8)

线程1进行到(2)。它开始评估head_->next。它将head_加载到寄存器中,然后放弃优先级。

线程2从函数的开始到结束进行。它通过删除它来删除head_,并返回head_

的内容

线程1醒来。它在head_字段中的->next后面跟随head_。但是线程2已经删除了printf指向的数据,我们只是按照悬空指针。

答案 1 :(得分:0)

我在阅读时遇到了同样的困惑,并试图用谷歌搜索答案...我找不到答案,最后去检查compare_exchange_weak参考。 我们缺少的部分是您传入第二个所需参数的时间,您已经取消引用了悬空指针... 您真的无法摆脱它,因为该函数需要知道您要传递的内容,从而取消引用它。