2014-12-06 42 views
11

在x86架构上,存储到相同内存位置的订单总数有所不同,例如,请参见this video。 C++ 11内存模型有哪些保证?对于不同线程中相同位置的两次宽松写入操作,是否总能以其他线程的相同顺序看到?

更确切地说,在

-- Initially -- 
std::atomic<int> x{0}; 

-- Thread 1 -- 
x.store(1, std::memory_order_release); 

-- Thread 2 -- 
x.store(2, std::memory_order_release); 

-- Thread 3 -- 
int r1 = x.load(std::memory_order_acquire); 
int r2 = x.load(std::memory_order_acquire); 

-- Thread 4 -- 
int r3 = x.load(std::memory_order_acquire); 
int r4 = x.load(std::memory_order_acquire); 

将结果r1==1, r2==2, r3==2, r4==1被允许(在x86之外的一些架构)?如果我要用std::memory_order_relaxed替换所有的memory_order

回答

7

不,这样的结果是不允许的。 §1.10[intro.multithread]/P8,18(引用N3936/C++ 14;相同的文字是在第6发现,16 N3337/C++ 11):

8所有修改到特定原子物体M发生在一些 特定总次序,被称为修饰顺序M.

18如果一个原子对象M的 值计算乙先于M发生,和的值计算A A取其值来自于M上的副作用X ,则由B计算的值应该是由 X存储的值或由M上的副作用Y存储的值,其中Y foll行X在 M的修改顺序。[注意:此要求被称为 读读连贯性。 - 注完]

在代码中有两个副作用,并通过他们P8发生在一些特定的总订单。在线程3中,计算要存储在r1中的值的计算值发生在r2之前,因此给出r1 == 1r2 == 2我们知道线程1执行的存储位于由线程2执行的存储之前,其修改顺序为x。在这种情况下,Thread 4无法观察到r3 == 2, r4 == 1而不会与p18发生冲突。这与使用的memory_order无关。

有在p21基因的说明(在N3337 P19),其是相关的:

[:四个前面的相干性要求的原子操作的有效 不允许编译器重新排序,以一个单一的对象, 甚至如果两种操作都是放松的负载。这有效地使大多数硬件提供的对C++ 原子操作提供的高速缓存一致性保证。 - 注完]

+0

你能帮我理解p18吗? “价值计算”的代名词是来自原子的加载和“副作用”的同义词,它是存储之间的原子吗? – 2014-12-06 16:40:23

+0

@TobiasBrüll加载是一个值计算;该商店是一个副作用。 – 2014-12-06 16:42:30

+0

还有什么其他类型的值计算存在?还有什么其他类型的副作用? – 2014-12-06 16:49:50

4

每C++ 11 [intro.multithread]/6: “所有的修改以使特定原子对象M发生在一些特定的总顺序,称为M修饰顺序”因此,由特定线程读取原子对象永远不会看到比该线程已经观察到的更老的值。请注意,这里没有提及内存排序,所以这个属性适用于所有这些 - seq_cstrelaxed

在OP中给出的示例中,修改订单x可以是(0,1,2)(0,2,1)。已经以该修改顺序观察到给定值的线程不能观察到较早的值。结果r1==1, r2==2意味着修改订单x(0,1,2),但r3==2, r4==1意味着它是(0,2,1),这是一个矛盾。所以这个结果在符合C++ 11的实现上是不可能的。

相关问题