简体   繁体   English

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

[英]Does an atomic acquire synchronize with mutex lock release?

I have an object that stores some settings in an unordered_map with string keys and variant values.我有一个 object 将一些设置存储在unordered_map中,其中包含字符串键和变量值。 As my library might be used from several threads and reads will very likely outnumber writes by a large margin, I've thought about a copy on write implementation where the "get" operation is lock free and the "put" operation is in a critical section, as in the example:由于我的库可能会被多个线程使用,并且读取的数量很可能会大大超过写入的数量,因此我考虑了一种写时复制实现,其中“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);
    }
}

I am reasonably confident that the design works, as long as the acquire/release synchronization affects also the pointed-to data and not just the pointer value.只要获取/释放同步也会影响指向的数据而不仅仅是指针值,我有理由相信该设计是有效的。 However, my questions are as follows:但是,我的问题如下:

  • Is the atomic store within the lock required for this to work?这个工作需要锁内的原子存储吗?
  • Or will the atomic acquire synchronize with the mutex unlock which is a "release" operation?或者原子获取是否会与作为“释放”操作的互斥锁同步?

will the atomic acquire synchronize with the mutex unlock which is a "release" operation?原子获取是否会与作为“释放”操作的互斥锁同步?

No, in order for an acquire operation to synchronize-with a release operation, the acquire operation has to observe the changes of the release operation (or some change in a potential release sequence headed by that operation).不,为了使获取操作与释放操作同步,获取操作必须观察释放操作的变化(或以该操作为首的潜在释放序列的某些变化)。

So yes, you need the atomic store inside the lock.所以是的,你需要锁内的原子存储。 There is no guarantee that get will "see" the latest value from put since you only use acquire/release, so there is not total order between the store and load operations.由于您仅使用获取/释放,因此无法保证get将“看到”来自put的最新值,因此存储和加载操作之间没有总顺序。 If you want that guarantee you have to use memory_order_seq_cst .如果你想要这个保证,你必须使用memory_order_seq_cst

As a side-note - this implementation is most likely not lock-free, because in most library implementations atomic_load_explicit for shared_ptr is not lock-free.作为旁注 - 这个实现很可能不是无锁的,因为在大多数库实现中, shared_ptratomic_load_explicit不是无锁的。 The problem is that you have to load the pointer and dereference that pointer to increment the ref-counter, in one atomic operation .问题是您必须在一个原子操作中加载指针并取消引用该指针以增加 ref-counter。 This is not possible on most architectures, so atomic_load_explicit is usually implemented using a lock.这在大多数架构上是不可能的,因此atomic_load_explicit通常使用锁来实现。

It's required if you want your get function to always return the latest value.如果您希望您的get始终返回最新值,则需要它。 It can occur that you have multiple reads and write occurs in the same clock time.您可能会在同一时钟时间内发生多次读取和写入。 Using atomic memory order ensures the order that write is before read.使用原子 memory 顺序确保写入在读取之前的顺序。

If you mix non-atomic stores and atomic loads, it is undefined behavior.如果混合使用非原子存储和原子加载,这是未定义的行为。 This thread also discussed it.这个线程也讨论了它。 You potentially have one write after another write.您可能有一个又一个的写入。 You can have data race if you use non-atomic instruction.如果您使用非原子指令,您可能会遇到数据竞争。

According cppreference根据cppreference

memory_order_acquire 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 的所有访问(对加载线程有可见的副作用)发生后,该操作就会发生。

memory_order_release 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.该操作被安排在消费或获取操作之前发生,作为对 memory 的其他访问的同步点,这些访问可能对加载线程有明显的副作用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM