c++ - 将全局引用计数资源与原子同步——在这里放宽合适吗?

我有一个全局引用计数对象obj,我想通过使用原子操作来防止数据竞争:

T* obj;                  // initially nullptr
std::atomic<int> count;  // initially zero

我的理解是,我需要在写入obj 后使用std::memory_order_release,以便其他线程知道它正在创建:

void increment()
{
    if (count.load(std::memory_order_relaxed) == 0)
        obj = std::make_unique<T>();

    count.fetch_add(1, std::memory_order_release);
}

同样,我需要在读取计数器时使用std::memory_order_acquire,以确保线程可以看到正在更改的obj:

void decrement()
{
    count.fetch_sub(1, std::memory_order_relaxed);

    if (count.load(std::memory_order_acquire) == 0)
        obj.reset();
}

我不相信上面的代码是正确的,但我也不完全确定为什么。我觉得在 obj.reset() 被调用之后,应该有一个 std::memory_order_release 操作来通知其他线程。对吗?

是否还有其他可能出错的地方,或者我对这种情况下的原子操作的理解完全错误?

最佳答案

不管内存顺序如何都是错误的。

正如@MaartenBamelis 指出的increment 的并发调用,对象被构建了两次。并发 decrement 也是如此:对象被重置两次(这可能导致两次析构函数调用)。

请注意,在 T* obj; 声明和将其用作 unique_ptr 之间存在分歧,但是原始指针和唯一指针对于并发修改都是不安全的。实际上,resetdelete 将检查指针是否为空,然后删除并将其设置为空,这些步骤不是原子的。

fetch_addfetch_sub 是 fetch 和 op 而不仅仅是 op 是有原因的:如果你不使用操作过程中观察到的值,它很可能是一个种族。

https://stackoverflow.com/questions/71129379/

相关文章:

docker - 如何通过 SSH 进入 colima 实例

python - 两个日期时间之间的 15 分钟间隔数

javascript - react-bootstrap-table-toolkit 搜索导入报错

python - 我们如何提取数据框中具有顺序值的行?

javascript - 使用 useState 从数组中删除最后一项

python - 极地 : switching between dtypes within a Da

arrays - Perl 中是否有任何函数可以移动数组元素而不删除它们?

python - 文档字符串应该放在装饰器之前还是之后?

python - 理解 python 的 len() 时间复杂度

python - 如何在不完全用 Python 重写的情况下在函数内部添加、删除和编辑某些行?