其他线程是否会以相同的顺序看到两个对不同线程中相同位置的轻松写入?

时间:2014-12-06 15:43:15

标签: c++ c++11 concurrency memory-model stdatomic

在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以外的某些架构上)?如果我要memory_order替换所有std::memory_order_relaxed,该怎么办?

3 个答案:

答案 0 :(得分:9)

不,不允许这样的结果。 §1.10[intro.multithread] / p8,18(引用N3936 / C ++ 14;相同的文本见N3337 / C ++ 11的第6和16段):

  

8对特定原子对象M的所有修改都出现在某些原子对象中   特定的总订单,称为M的修改订单

     

18如果原子对象M的值计算A发生在a之前   M的值计算B,A从副作用X取其值   在M上,那么由B计算的值应该是存储的值   X或副作用Y在M上存储的值,其中Y跟随X in   M的修改顺序[注意:此要求称为   读 - 读一致性。 - 结束记录]

在您的代码中有两个副作用,而p8则以某种特定的总顺序出现。在线程3中,计算要存储在r1中的值的值计算发生在r2之前,因此给定r1 == 1r2 == 2我们知道存储由线程1在线程2以x的修改顺序执行的存储之前。既然如此,Thread 4如果不与p18发生冲突就无法观察r3 == 2, r4 == 1。这与使用的memory_order无关。

p21(N3337中的第19页)中有一条相关的注释:

  

[注意:有效的前四个一致性要求   禁止编译器将原子操作重新排序到单个对象,   即使两个操作都是放松的负载。这有效地使   缓存一致性保证由C ++可用的大多数硬件提供   原子操作。 - 结束记录]

答案 1 :(得分:5)

Per C ++ 11 [intro.multithread] / 6:&#34;对特定原子对象M的所有修改都以某种特定的总顺序出现,称为M的修改顺序。 &#34;因此,特定线程对原子对象的读取永远不会看到更老的&#34;值比线程已经观察到的值。请注意,这里没有提及内存排序,所以这个属性适用于所有这些属性 - 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的实现上无法获得结果。

答案 2 :(得分:0)

鉴于C ++ 11规则绝对不允许这样做,这里有一种更为定性/直观的理解方式:

如果x没有其他商店,最终所有读者都会同意其价值。 (即两家商店中的一家排名第二)。

如果不同的线程可能不同意订单,那么他们要么永久/长期不同意该值,要么一个线程可以看到值更改第3个额外时间(幻像存储)

幸运的是,C ++ 11并不允许这两种可能性。