c ++ 11 std :: atomic compare_exchange_weak和堆栈容器

时间:2013-06-24 18:39:52

标签: c++ c++11

我正在通过Anthony Williams Concurrency for C ++ 11。

我对锁定自由堆栈的弹出实现感到有点困惑。

template<typename T>
class lock_free_stack
{
private:
struct node
{
    std::shared_ptr<T> data;
    node* next;
    node(T const& data_): data( std::make_shared<T>(data_)){}
};

std::atomic<node*> head;

public:

void push(T const& data)
{
    node* const new_node = new node(data);
    new_node->next = head.load();
    while(!head.compare_exchange_weak(new_node->next, new_node));
}

std::shared_ptr<T> pop()
{
    node* old_head = head.load();
    while( old_head && !head.compare_exchange_weak(old_head, old_head->next));

            // Does This not return the data of the next node before the head if 
            // compare_exchange_weak returns true
    return old_head ? old_head->data : std::shared_ptr<T>();
}
};

导致我混淆的一行是pop函数的最后两行。

    while( old_head && !head.compare_exchange_weak(old_head, old_head->next));
    return old_head ? old_head->data : std::shared_ptr<T>();

如果compare_exchange_weak函数返回true,那么它是否会将old_head更改为堆栈中的下一个节点?当old_head不是nullptr时,这将导致return语句从下一个节点返回数据而不是堆栈顶部的旧头。

我是否错误地解释了这个?

2 个答案:

答案 0 :(得分:3)

除了我的评论,如果compare_exchange_weak成功,它设法将head从值old_head更新为值old_head->next,因此old_head不再是列表的一部分,因此可以正确归还。

仅当它返回false时才会修改old_head

的值

编辑:显示上述代码的问题。

首先,如果2个主题进入pop并且都读取head(通过head.load())并获得相同的old_head

线程一被换出(比方说)

线程2继续运行,弹出头并返回调用者,然后调用者删除保存节点的值。

线程1然后恢复并尝试阅读old_head->next甚至调用 compare_exchange_weak。

但是,old_head指向已被删除的内存。未定义的行为,如果你是段错误的话,你很幸运。

其次,这有经典的ABA问题。我不打算解释这个,因为它有充分的记录和理解。搜索它。

答案 1 :(得分:1)

head.compare_exchange_weak返回false时,它会修改old_head

当它返回true时,它会修改head并且不会修改old_head

请参阅http://en.cppreference.com/w/cpp/atomic/atomic/compare_exchange和/或http://cpp0x.centaur.ath.cx/atomics.types.operations.req.html#p21