c++11 std::atomiccompare_exchange_weak和堆栈容器

c++11 std::atomic compare_exchange_weak and the stack container

本文关键字:堆栈 exchange std atomiccompare c++11 weak      更新时间:2023-10-16

我正在为C++11编写Anthony Williams Concurrency一书。

我对无锁堆栈的pop实现有点困惑。

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语句从下一个节点返回数据,而不是从堆栈顶部的旧头返回数据。

我把这个解释错了吗?

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

只有当它返回false时,它才会修改old_head 的值

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

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

线程1被换出(比如)

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

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

但是,old_head指向已删除的内存。未定义的行为,如果你犯错,那你就是幸运的。

其次,这有典型的ABA问题。我不想解释,因为它有很好的记录和理解。搜索它。

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