原子获取是否与互斥锁释放同步?

Does an atomic acquire synchronize with mutex lock release?

我有一个对象将一些设置存储在 unordered_map 中,其中包含字符串键和变体值。由于我的库可能被多个线程使用,并且读取次数很可能大大超过写入次数,所以我考虑过写时复制实现,其中 "get" 操作是无锁的,而 "put" 操作处于关键部分,如示例所示:

class Cfg {
    using M = unordered_map<string,X>;
    shared_ptr<const M> data;
    mutex write_lock;
public:
    X get(string key) {
        shared_ptr<const M> cur_ver = atomic_load_explicit(&data, memory_order_acquire);
        // Extract the value from the immutable *cur_ver
    }
    void put(string key, X value) {
        lock<muted> wlock(write_lock);
        // No need for the atomic load here because of the lock
        shared_ptr<const M> cur_ver = data;
        shared_ptr<const M> new_ver = ;// create new map with value included
        // QUESTION: do I need this store to be atomic? Is it even enough?
        atomic_store_explicit(&data, new_ver, memory_order_release);
    }
}

只要 acquire/release 同步还影响指向的数据而不仅仅是指针值,我有理由相信该设计有效。但是,我的问题如下:

如果您希望 get 函数始终 return 最新值,则需要它。您可能会在同一时钟时间内进行多次读取和写入。使用原子内存顺序确保写入先于读取的顺序。

如果混合使用非原子存储和原子加载,这是未定义的行为。这个thread也讨论过了。您可能会一个接一个地写。如果你使用非原子指令,你可以有数据竞争。

根据cppreference

memory_order_acquire

The operation is ordered to happen once all accesses to memory in the releasing thread (that have visible side effects on the loading thread) have happened.

memory_order_release

The operation is ordered to happen before a consume or acquire operation, serving as a synchronization point for other accesses to memory that may have visible side effects on the loading thread.

will the atomic acquire synchronize with the mutex unlock which is a "release" operation?

不,为了使获取操作与释放操作同步,获取操作必须观察释放操作的变化(或潜在的一些变化以该操作为首的释放序列)。

所以是的,您需要锁内的原子存储。无法保证 get 将 "see" 来自 put 的最新值,因为您只使用 acquire/release,因此存储和加载操作之间没有全序。如果你想要保证你必须使用 memory_order_seq_cst.

作为旁注 - 此实现很可能不是无锁的,因为在大多数库实现中 atomic_load_explicit for shared_ptr 不是无锁的。问题是您必须加载指针并取消引用该指针以增加引用计数器,在一个原子操作中。这在大多数架构上是不可能的,所以 atomic_load_explicit 通常使用锁来实现。