混合松弛和释放-获取内存指令

Cru*_*ean 5 c++ multithreading synchronization atomic stdatomic

考虑std::atomic<int> x(0)。如果我理解正确,则std::memory_order_relaxed仅保证操作以原子方式发生,但不提供同步保证。因此x.fetch_add(1, std::memory_order_relaxed),来自 2 个线程的 1000 次将始终具有 2000 的最终结果。但是,这些调用中的任何一个的返回值都不能保证反映真实的当前值(例如,第 2000 次增量可能返回 1700 作为前一个值)。

但是 - 这就是我的困惑 - 鉴于这些增量是并行发生的,会x.load(std::memory_order_acquire)返回什么?或者x.fetch_add(1, std::memory_order_acq_rel)?这些是否返回真实的当前值,或者它们是否具有由于宽松的增量而导致的宽松排序所具有的过时答案的相同问题?

据我所知,该标准仅保证释放到获取(在同一变量上)同步并因此给出真实的当前值。那么如何轻松混合典型的获取-释放语义呢?

例如,我听说std::shared_ptr的引用计数以宽松的顺序递增并以 acq_rel 的顺序递减,因为它需要确保它具有真实值以便只删除一次对象。因此,我很想认为他们会给出真实的当前值,但我似乎找不到任何标准来支持它。

Pet*_*des 2

ISO C++ 保证每个原子对象都单独存在修改顺序。

使用 seq_cst 可以保证有一个全局顺序,所有线程都可以就a之前的更改b或其他内容达成一致。但对于单个对象,即使放宽某些操作,也保证存在修改顺序。

fetch_add您从轻松定义/记录修改顺序的 返回值。根据定义,第 2000 个增量返回,这就是您知道它是第 2000 个的方式。2000

据我所知,该标准仅保证释放到获取(在同一变量上)同步,从而给出真实的当前值。

仅当您关心读取其他值时才需要 Synchronizes-with,例如一个线程存储到非原子数组,然后执行类似data_ready = 1;. 为了让读者安全地从数组中读取数据,他们需要查看data_ready != 0获取负载,这意味着他们还可以看到执行释放存储的线程中所有早期分配的效果。