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

hjzp0vay  于 2022-11-19  发布在  其他
关注(0)|答案(2)|浏览(234)

我想知道如何为非原子对象有效地实现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

o4tp2gmn

o4tp2gmn1#

实现与std::atomic<T>本身几乎 * 完全 * 相同。这不是一个新问题。
参见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<T>有足够的对齐时才起作用,但在许多32位平台上(包括x86),uint64_t可能只有4字节对齐。这样的对象上的atomic_ref将编译并运行,但如果编译器在32位模式下使用SSE 8字节加载/存储来实现它,则实际上不是原子的。对于具有alignof(T) == sizeof(T)的对象(如64位体系结构上的大多数基本类型)来说没有危险。
这就是为什么你需要用所需的对齐来分配底层的非原子对象,例如。

alignas(std::atomic_ref<T>::required_alignment) T foo;

或 * 检查 * 其必须已经充分对齐,例如

static_assert( std::atomic_ref<T>::required_alignment) == alignof(T), "T isn't *guaranteed* aligned enough for atomic_ref" );

请参阅https://en.cppreference.com/w/cpp/atomic/atomic_ref/required_alignment

n6lpvg4x

n6lpvg4x2#

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

相关问题