用于引用计数的释放-消耗排序

Ziz*_*Tai 3 c++ multithreading reference-counting memory-model stdatomic

考虑以下简单的引用计数函数(与 一起使用boost::intrusive_ptr):

\n\n
class Foo {\n    // ...\n\n    std::atomic<std::size_t> refCount_{0};\n\n    friend void intrusive_ptr_add_ref(Foo* ptr)\n    {\n        ++ptr->refCount_;  // \xe2\x9d\xb6\n    }\n\n    friend void intrusive_ptr_release(Foo* ptr)\n    {\n        if (--ptr->refCount_ == 0) {  // \xe2\x9d\xb7\n            delete ptr;\n        }\n    }\n};\n
Run Code Online (Sandbox Code Playgroud)\n\n

我仍在学习内存排序,我想知道在这种情况下fetch_add/sub( memory_order_seq_cst) 的默认内存排序是否太严格。由于我想确保的唯一顺序是在 \xe2\x9d\xb6 和 \xe2\x9d\xb7 之间,我认为我们可以将 \xe2\x9d\xb6 替换为

\n\n
ptr->refCount_.fetch_add(1, std::memory_order_release);\n
Run Code Online (Sandbox Code Playgroud)\n\n

和 \xe2\x9d\xb7 与

\n\n
if (ptr->refCount_.fetch_sub(1, std::memory_order_consume) == 1) {\n
Run Code Online (Sandbox Code Playgroud)\n\n

但内存排序对我来说仍然是新的和微妙的,所以我不确定这是否能正常工作。我错过了什么吗?

\n

Pot*_*ter 5

参考 的 libc++ 实现std::shared_ptr,您可能需要memory_order_relaxed增量和memory_order_acq_rel递减。合理化这种用法:

如果数量增加,那么最重要的是其一致性。当前线程已经确定它大于零。其他线程是不同步的,因此它们将在下一次原子修改之前的不确定时间看到更新,并且可能与其他变量的更新不一致。

如果数量减少,那么您需要确保当前线程已经完成对其的修改。来自其他线程的所有更新都必须可见。当前的减量必须对下一个减量可见。否则,如果计数器跑到它所守护的物体之前,则该物体可能会过早被摧毁。

Cppreference有一个关于内存排序的很好的页面。它包括以下注释:

释放-消费排序规范正在修订,暂时不鼓励使用memory_order_consume

它还暗示当前的编译器或 CPU 都没有实现consume;它实际上与 相同acquire