首页 > 解决方案 > 混合松弛和释放-获取内存顺序

问题描述

考虑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 的顺序递减,因为它需要确保它具有真实值才能只删除一次对象。因此,我很想他们会给出真实的当前值,但我似乎找不到任何标准来支持它。

标签: c++multithreadingsynchronizationatomicstdatomic

解决方案


ISO C++ 保证每个原子对象分别存在一个修改顺序。

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

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

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

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


推荐阅读