用C ++重新排序原子操作

时间:2016-10-29 14:46:52

标签: c++ concurrency atomic

假设我有2个帖子:

int value = 0;
std::atomic<bool> ready = false;

thread 1:
value = 1
ready = true;

thread 2:
while (!ready);
std::cout << value;

这个程序能输出0吗?

我读到了关于C ++内存模型 - 特别是顺序一致性,我认为这是默认的,并不是特别清楚。编译器是否只需要以相对于彼此的正确顺序放置原子操作,或者是否需要相对于所有其他操作以正确的顺序放置原子操作?

5 个答案:

答案 0 :(得分:4)

默认情况下,使用memory_order_seq_cst语义对原子变量进行操作,这保证不会进行重新排序。

因此,行value = 1无法在原子赋值之下重新排序:value = 1,因此行std::cout << value;将始终打印1。

按照相同的规则,行std::cout << value;无法重新排序 在线上方:while (!ready);

答案 1 :(得分:1)

根据ShadowRanger的回应,它就像一个记忆障碍。 但是,有关其原因的详细信息,我建议您查看Herb Sutter's talk on atomic weapons。他详细介绍了原子如何以及为什么会起作用。

答案 2 :(得分:0)

  

该程序能够输出0吗?

您的推理是正确的。在ISO C ++中,seq_cst和acq_rel原子可以在线程之间的关系之前/之后创建事件,从而使一个线程可以安全地写入非原子变量,然后另一个线程无需数据争用UB即可读取该变量。

在这种情况下,您已经正确地做到了:spin-wait循环是标志的seq-cst加载,只有在看到true值时才退出循环。非原子value的评估发生在看到true的负载之后。并且在编写器中,顺序发布存储可确保在值存储之前看不到标志存储。


在为常规ISA编译为asm时,编译器必须遵守发布发布之前非原子存储的顺序,以及获取负载之后非原子负载的顺序。除非能够以某种方式证明对于其他可能的线程,仍然存在UB。

答案 3 :(得分:-2)

是的,但仅是因为您使用了默认设置。

Cst受苦是因为它使用全局范围进行重新排序;这是旧架构的产物。较新的体系结构具有更大的排序范围,因此您可能希望标准更新在不久的将来会使您的代码无效。

写入队列最终可能包含数十个条目,每个条目的解析度对总线的延迟都非常大。将它们划分为重要的与不重要的是显而易见的一步,这是新架构已经体现的一步。

C ++ 标准创建委员会显然已经超出其深度,应该停止发明无用的废话。

答案 4 :(得分:-3)

第一个注意事项:由于存在UB(不确定行为)的可能性,因为没有明确定义的抽象线程语义,所以绝对不可能推断出支持线程的任何C和C ++版本中的任何C或C ++程序。任何定义的语义。这是C和C ++语义上的又一主要理论和实践缺陷(除许多其他严重缺陷之外)。

但是您可以用实际的理由进行推理:编译器在执行线程原语时非常可预测(将来可能不会这样,因为编译器编写者会熟练使用线程语义并开始使用UB的声明破坏事物)。 / p>

使用线程通信原语时,编译器会做正确的事情以保证信息流。 while (!ready);确保在设置了原子对象之后 线程退出循环:存在一个定义明确的“过去”。

作为现实世界中定义不正确的“过去”的示例,请记住与宇航员在休斯敦交谈时进行的阿波罗音频交换:由于宇航员距离很远,所以没有明确定义谁首先开始交谈的概念,并且是唯一的我们从休斯敦(Houston)获得的记录显示了一个命令,但从飞船进行的假设记录将显示另一个,而这两个命令都不正确。宇航员和休斯顿开始无秩序地谈话,这两个都不是过去。除非您明白这一点,否则您无法声称自己理解相对论。

使用多线程技术,您可以进行的内存操作不是过去的,并且不知道如果他们尝试使用非过去操作的对象,他们将观察到什么。