首页 > 解决方案 > std::atomic_ref 如何为非原子类型实现?

问题描述

我想知道如何为非原子对象std::atomic_ref有效地实现(std::mutex每个对象一个),因为以下属性似乎很难强制执行:

通过 atomic_ref 应用于对象的原子操作相对于通过引用同一对象的任何其他 atomic_ref 应用的原子操作是原子的。

特别是以下代码:

void set(std::vector<Big> &objs, size_t i, const Big &val) {
    std::atomic_ref RefI{objs[i]};
    RefI.store(val);
}

似乎很难实现,因为std::atomic_ref每次都需要以某种方式选择相同的std::mutex(除非它是同一类型的所有对象共享的大主锁)。

我错过了什么吗?或者每个对象都负责实现std::atomic_ref,因此要么是原子的,要么带有std::mutex?

标签: c++c++20stdatomicstdmutex

解决方案


实现与它本身几乎完全相同std::atomic<T>。这不是一个新问题。

请参阅std::atomic 的锁在哪里?std::atomic/std::atomic_ref用于非无锁对象的锁的静态哈希表的 典型实现,按地址索引。哈希冲突只会导致额外的争用,而不是正确性问题。(死锁仍然是不可能的;锁只被原子函数使用,它们从不尝试一次取 2 个。)

例如,在 GCC 上, std::atomic_ref这只是__atomic_store在对象上调用的另一种方式。(参见GCC 手册:atomic builtins

编译器知道是否T小到可以无锁。如果没有,它会调用将使用锁的 libatomic 库函数。

(有趣的事实:这意味着它仅在对象具有足够的对齐方式时才有效atomic<T>。但在包括 x86 在内的许多 32 位平台上,uint64_t可能只有 4 字节对齐。 atomic_ref在这样的对象上将编译和运行,但实际上不是原子的编译器在 32 位模式下使用 SSE 8 字节加载/存储来实现它。幸运的是,对于具有 的对象没有危险alignof(T) == sizeof(T),就像 64 位架构上的大多数原始类型一样。)


推荐阅读