【问题标题】:Mixing Relaxed and Release-Acquire Memory Orders混合松弛和释放-获取内存顺序
【发布时间】:2019-06-25 12:03:33
【问题描述】:

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

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

据我所知,该标准仅保证释放到获取(在同一变量上)同步,从而提供真实的当前值。那么如何轻松地与典型的获取-释放语义相结合呢?

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

【问题讨论】:

  • 我很漂亮,但不完全确定(因此评论而不是回答),轻松同步有问题的变量,因此您将始终拥有正确的值。它只是不能用于同步调用周围的任何else。对于shared_ptr,您可以放宽增量(因为您在增量之前或之后什么都不做),但是您必须使用 acq_rel 语义减量,因为在释放到非零时,您必须释放对它指向的对象所做的任何写入,如果您是您还必须获取最终的减量器(为零),以便在运行析构函数/释放内存之前获得其他线程写入。
  • 了解内存模型的最简单方法是: 1. 假设代码按原样执行(即忽略重新排序); 2. 当您的代码修改内存时,假设其他线程将在可预测的时间以不可预测的顺序观察修改——这是relaxed 模型。现在阅读其他模型——它们只是为其他线程的观察(您的更改)顺序提供额外的保证。
  • 嗯...我在这里可能不完全正确:)。检查this discussionthis
  • @C.M.对,看到我得到了放松和其他人之间的区别,但我似乎在标准中找不到任何东西,使得在同一个变量上混合放松/非放松根据非放松看到的定义明确当前值。像 - 如果在其他线程中更新了宽松存储,使用获取读取仍然会看到当前值吗?
  • @CruzJean 我的看法:atomic fetch_add 对可见性顺序施加了额外的隐含要求——保证始终观察最新值并以原子方式更新它(或者换句话说,在IA-32 甚至宽松的 fetch_add 都会使用 LOCK 前缀)。所以,是的——你可以同时使用它(用于递增和递减),但递减的问题是最后一个--ref 紧接着是 dtor 调用,并且该调用必须以递减的方式正确排序(或者它可能发生在之前) -- 因此要求对最终引用计数减量使用更严格的排序

标签: c++ multithreading synchronization atomic stdatomic


【解决方案1】:

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

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

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

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-02-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多