std::atomic_ref 如何为非原子类型实现?

Posted

技术标签:

【中文标题】std::atomic_ref 如何为非原子类型实现?【英文标题】:How is std::atomic_ref implemented for non-atomic types? 【发布时间】:2020-02-09 06:42:54 【问题描述】:

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

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

特别是以下代码:

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

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

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

【问题讨论】:

他们可能有一个地址和互斥锁的映射,以及与对象地址相关的互斥锁的外观。这允许多个不同的引用来保护单个对象。 【参考方案1】:

实现可以使用基于对象地址的散列来确定在执行操作时要获取一组锁中的哪一个。

【讨论】:

这实际上是 libstdc++ 在shared_ptr 对象上实现原子操作的方式。 github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/src/… 说哈希表会包含互斥体?他们什么时候可以安全释放? @Nonyme 在我提供的链接中,您可以看到它们具有静态存储持续时间。【参考方案2】:

实现与std::atomic&lt;T&gt; 本身几乎完全相同。这不是一个新问题。

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

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

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

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

【讨论】:

我没有意识到atomic&lt;T&gt; 也允许用于非原子类型(尽管从技术上讲,它可以自己分配mutex,因为它拥有该对象)。谢谢解释,有道理。

以上是关于std::atomic_ref 如何为非原子类型实现?的主要内容,如果未能解决你的问题,请参考以下文章

如何为 .NET 压缩配置 Xceed Real-Time 以允许非 ascii 文件名?

如何为用户定义的类型专门化 std::hash<T>?

我如何为数组中的原子变量调用 compare_exchange_weak(0,1)?

如何为 SQL 表自动生成数据类型

如何为 Sequence[T] 提供泛型类型参数?

正则表达式语法