在一些论坛和书籍(即C++ Concurrency in Action)中,有一个很好的多生产者/多消费者堆栈示例,并且在pop实现中他们通常这样做以下:
// head is an std::atomic<node*> variable
node *old_head = head.load();
while(old_head && !head.compare_exchange_weak(old_head, old_head->next));
...
为什么使用 std::atomic这么说吧:
- old_head->next 在线程被抢占之前(就在compare_exchange_weak之前)获得resolved
- 然后 BA 场景就会发生
- 线程恢复后head == old_head有效;在这种情况下,old_head->next 不会再次解析,指向无效的内存位置
- compare_exchange_weak 将被执行并通过,但值 old_head->next 仍将是旧值
然后就会发生 ABA 相关问题。
我相信我错过了一些东西。我在这里缺少什么?
干杯
最佳答案
是的,你会在这里遇到 ABA 问题。
不过,我认为这并不重要,因为您所指的实现(CiA 中的列表 7.3)无论如何都是无效的,它正在泄漏节点。
如果我们看一下最简单的引用计数实现,即使用无锁 std::shared_ptr
(CiA 中的列表 7.9)的实现,我们会发现问题不会发生。当使用共享指针时,old_head不会被删除,因为我们的线程仍然保留着对它的引用,因此新创建的头无法在旧头的内存地址中创建。
官方也有一个帖子Concurrency in Action Manning forums关于堆栈实现中的ABA问题。
关于c++ - 为什么 std::atomic<T>::compare_exchange_* 不应遭受 ABA 问题?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20173766/