C ++内存模型中的哪些确切规则可以防止在获取操作之前重新排序? [英] What exact rules in the C++ memory model prevent reordering before acquire operations?

查看:93
本文介绍了C ++内存模型中的哪些确切规则可以防止在获取操作之前重新排序?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我对以下代码中的操作顺序有疑问:

I have a question regarding the order of operations in the following code:

std::atomic<int> x;
std::atomic<int> y;
int r1;
int r2;
void thread1() {
  y.exchange(1, std::memory_order_acq_rel);
  r1 = x.load(std::memory_order_relaxed);
}
void thread2() {
  x.exchange(1, std::memory_order_acq_rel);
  r2 = y.load(std::memory_order_relaxed);
}

给出cppreference页面上std::memory_order_acquire的说明( https://en. cppreference.com/w/cpp/atomic/memory_order ),即

Given the description of std::memory_order_acquire on the cppreference page (https://en.cppreference.com/w/cpp/atomic/memory_order), that

具有此内存顺序的加载操作将在受影响的内存位置上执行获取操作:在此加载之前,无法重新排序当前线程中的读取或写入.

A load operation with this memory order performs the acquire operation on the affected memory location: no reads or writes in the current thread can be reordered before this load.

很明显,同时运行thread1thread2之后,永远不会出现r1 == 0 && r2 == 0的结果.

it seems obvious that there can never be an outcome that r1 == 0 && r2 == 0 after running thread1 and thread2 concurrently.

但是,我找不到C ++标准中的任何用语(现在看一下C ++ 14草案),该用语确保了两个宽松的负载不能通过并购发布交换进行重新排序.我想念什么?

However, I cannot find any wording in the C++ standard (looking at the C++14 draft right now), which establishes guarantees that two relaxed loads cannot be reordered with acquire-release exchanges. What am I missing?

如评论中所建议,实际上有可能使r1和r2都等于零.我已经更新程序以使用load-acquire,如下所示:

As has been suggested in the comments, it is actually possible to get both r1 and r2 equal to zero. I've updated the program to use load-acquire as follows:

std::atomic<int> x;
std::atomic<int> y;
int r1;
int r2;
void thread1() {
  y.exchange(1, std::memory_order_acq_rel);
  r1 = x.load(std::memory_order_acquire);
}
void thread2() {
  x.exchange(1, std::memory_order_acq_rel);
  r2 = y.load(std::memory_order_acquire);
}

现在可以同时执行thread1thread2后使r1r2等于0吗?如果没有,那么哪个C ++规则可以阻止这种情况?

Now is it possible to get both and r1 and r2 equal to 0 after concurrently executing thread1 and thread2? If not, which C++ rules prevent this?

推荐答案

该标准并未定义C ++内存模型的方式,即如何围绕具有特定排序参数的原子操作对操作进行排序. 取而代之的是,对于获取/释放排序模型,它定义了正式的关系,例如与……同步"和在……之前发生",这些关系指定了如何在线程之间同步数据.

The standard does not define the C++ memory model in terms of how operations are ordered around atomic operations with a specific ordering parameter. Instead, for the acquire/release ordering model, it defines formal relationships such as "synchronizes-with" and "happens-before" that specify how data is synchronized between threads.

N4762,第29.4.2节-[atomics.order]

N4762, §29.4.2 - [atomics.order]

对原子对象M执行释放操作的原子操作A与对M执行获取操作的原子操作B同步 并从以A为首的释放顺序中的任何副作用中获取其价值.

An atomic operation A that performs a release operation on an atomic object M synchronizes with an atomic operation B that performs an acquire operation on M and takes its value from any side effect in the release sequence headed by A.

在第6.8.2.1-9节中,该标准还规定,如果存储A与负载B同步,则在A线程之前排序的任何内容都会发生在...之前".

In §6.8.2.1-9, the standard also states that if a store A synchronizes with a load B, anything sequenced before A inter-thread "happens-before" anything sequenced after B.

在第二个示例中(第一个示例甚至更弱),没有建立与...同步"(因此在线程间发生事前)关系,因为缺少运行时关系(用于检查负载的返回值).
但是即使您确实检查了返回值,也没有用,因为exchange操作实际上并没有释放"任何内容(即在这些操作之前没有存储操作被排序). Neiter不会执行原子加载操作获取"任何东西,因为在加载之后没有顺序进行操作.

No "synchronizes-with" (and hence inter-thread happens-before) relationship is established in your second example (the first is even weaker) because the runtime relationships (that check the return values from the loads) are missing.
But even if you did check the return value, it would not be helpful since the exchange operations do not actually 'release' anything (i.e. no memory operations are sequenced before those operations). Neiter do the atomic load operations 'acquire' anything since no operations are sequenced after the loads.

因此,根据标准,两个示例(包括0 0)中载荷的四个可能结果中的每一个都是有效的. 实际上,该标准给出的保证在所有操作上都不比memory_order_relaxed强.

Therefore, according to the standard, each of the four possible outcomes for the loads in both examples (including 0 0) is valid. In fact, the guarantees given by the standard are no stronger than memory_order_relaxed on all operations.

如果要在代码中排除0 0结果,则所有4个操作都必须使用std::memory_order_seq_cst.这样可以保证所涉及操作的总顺序.

If you want to exclude the 0 0 result in your code, all 4 operations must use std::memory_order_seq_cst. That guarantees a single total order of the involved operations.

这篇关于C ++内存模型中的哪些确切规则可以防止在获取操作之前重新排序?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆