使用memory_order_acquire但没有匹配的memory_order_release的C++原子

5vf7fwbs  于 2023-02-10  发布在  其他
关注(0)|答案(1)|浏览(115)

是否存在这样的情况:我们希望将一个带有memory_order_acquire的原子加载到带有memory_order_release的同一个原子上,而不进行相应的存储?
例如,如果我有这样一段代码:

std::atomic<uint64_t> state;
std::atomic<uint64_t> count;

// Thread 1
while (true)
{
  auto expected = state.load(memory_order_relaxed);
  auto desired = get_desired(expected);
  if (state.compare_exchange_weak(expected, desired, memory_order_relaxed, memory_order_relaxed) { <----- (A)
    count.fetch_add(1, memory_order_relaxed); <---- (B)
    break;
  }
}

// Thread 2
auto state = state.load(memory_order_acquire); <----- (C)
auto count = count.load(memory_order_relaxed); <----- (D)

线程2中的memory_order_acquire应该防止count的加载在state的加载之前移动,但是由于我们在其他地方都使用memory_order_relaxed,我说线程2可能会看到count的更新值,该值可能比state更新,即看到(B)才能看到(A)中compare_exchange的效果?
如果我想让线程2在(C)中加载(A)时始终可以看到它的效果,那么我是否应该将(B)更改为memory_order_release,在(D)中使用memory_order_acquire,并颠倒(C)和(D)的顺序?

kokeuurv

kokeuurv1#

线程2中的memory_order_acquire应防止count的加载在state的加载之前移动
不可以。C++中的原子内存排序并不是从防止某些操作彼此重新排序的Angular 来定义的。相反,它是从要求某些副作用在某些点可见的Angular 来定义的。特别是,

  • 如果线程A对原子变量执行释放-存储,
  • 而线程B对同一原子变量执行获取加载,
      • 和**线程B的获取加载读取线程A的释放存储存储的值;
      • 则**,保证线程A在释放-存储之前执行的所有副作用在获取-加载之后可由线程B观察到。

如果只有获取加载而没有释放存储,那么就没有这样的排序保证。
我说线程2可能[...]在看到来自(A)的compare_exchange的效果之前看到(B)的效果,对吗?
是的。
如果我想让线程2在(C)中加载(A)时始终可以看到它的效果,那么我是否应该将(B)更改为memory_order_release,在(D)中使用memory_order_acquire,并颠倒(C)和(D)的顺序?
是的。假设我没有理解错,在您执行了所描述的转换序列之后,我们将得到

  • 在线程1中:到statecompare_exchange_weak的松弛存储,随后是到countfetch_add的释放存储
  • 在线程2中:从count获取负载,然后从state释放负载

假设来自count的acquire-load读取线程1存储的值,则线程2中来自state的松弛加载必须观察线程1存储的值 * 或更晚的值 *。

相关问题