谁能解释一下((a == 1 && a == 2 && a == 3)== true)?
Posted
技术标签:
【中文标题】谁能解释一下((a == 1 && a == 2 && a == 3)== true)?【英文标题】:Can anyone explain how ((a == 1 && a == 2 && a == 3) == true)? 【发布时间】:2018-10-02 17:20:15 【问题描述】:根据输出的信息,谁能解释下面的代码?
(a==1 && a==2 && a==3)
怎么可能都是真的?
#include <iostream>
#include <thread>
int a = 0;
int main()
std::thread runThread([]() while (true) a = 1; a = 2; a = 3; );
while (true)
if (a == 1 && a == 2 && a == 3)
std::cout << "Hell World!" << std::endl;
输出:
Hell World! Hell World! Hell World! Hell World! Hell World! Hell World! Hell World! Hell World! Hell World! Hell World! Hell World! Hell World! Hell World! Hell World! Hell World! Hell World! Hell World!
...
2019 年 1 月
我认为这个问题与这个链接高度相关 -> C++11 introduced a standardized memory model. What does it mean? And how is it going to affect C++ programming?
【问题讨论】:
“可能”是什么意思?程序做它做的事情。你到底期待什么? 通常是“Hello World!” :-) @Tryer 你的意思是“地狱世界!”? @pm100 这具有未定义的行为,因此它可能随时为true
。似乎它实际上可能发生在 OP 的案例中。
@FrançoisAndrieux 如果教授试图让学生体验有关比赛条件和未定义行为的经验,那么这是一个很好的例子。
【参考方案1】:
您遇到了未定义的行为。
您的代码存在竞争条件;一个线程正在读取a
,而另一个正在写入,并且没有发生同步。你不能在 C++ 中这样做。执行此操作的程序可以编译为执行任何操作。
事实上,在发布版本中,我希望它编译为if(false)
。编译器优化了主线程,注意到没有同步,证明没有UB,a不能是3个不同的值,优化出if
。
在调试版本中,我可以预料到您会看到这些症状。不是因为它更正确,而是因为调试构建往往不会绊倒那种未定义的行为。
因此,要合理地谈论您的程序,您首先要做的就是删除未定义的行为:将a
设为std::atomic<int>
而不是int
。
现在,在发布和调试中,您都希望看到……您的测试所显示的内容。或者什么都没有。或者介于两者之间的任何东西。结果不再是未定义的,而是不确定的。
if
语句不是原子的。在这些条件之间,a
可以更改。对于一个永远运行的程序,它有时应该会发生,因为另一个线程正在改变它。
生成的程序定义明确。 Even forward progress guarantees 没问题,因为你读取了一个原子变量。
【讨论】:
【参考方案2】:您可能在禁用优化的情况下编译了您的程序,因此您正在编译的目标体系结构的程序集/机器代码实际上按照它们出现的顺序执行了 C++ 抽象机器中的所有步骤,包括实际存储或加载到/从内存。 (调试版本通常将每个变量视为volatile
。)
在 32 位 int
加载/存储自然是原子的典型架构上,调试构建的行为很像使用 std::atomic<int> a
和 a.store(1, std::memory_order_relaxed)
的可移植 C++ 程序,没有未定义的行为(或在 x86 上,@987654325 @)。
程序启动一个线程,将 a 的值反复设置为 1,然后是 2,然后是 3。(这是 runThread 行)。
然后主线程测试 a 是否等于 1,a 是否等于 2,a 是否等于 3。如果这三个都等于,则打印“hello world”。 (这发生在第二个while(true)
)
它打印“hello world”的原因是并发性。碰巧当一个线程执行三个测试时,另一个线程恰好在正确的时间写入正确的值。记住if
的三个部分是一个接一个完成的
这无法保证发生。如果您不进行调试构建,则可以对其进行优化。但是考虑到计算机的运行速度以及线程的典型实现,它确实会发生。
不要依赖这种行为。相反,请尝试理解这两个线程同时执行操作。
【讨论】:
这个答案解释了未定义行为的一个可能结果,但程序可以做任何事情。我实际上希望if
条件被优化为始终为false
,因此它永远不会打印。虽然这也只是另一种可能性。
是的,我意识到这个论坛非常强烈地告诉“这是 UB,不要这样做”,并且强烈而一致。不过,我希望它对 OP 了解 如何 它可能发生是有用的。识别 UB 是一项技能。看到可能的线程流是另一回事。
@FrançoisAndrieux:我更新了这个答案来解释为什么 UB 变成了以这种方式工作的机器代码,用于在“普通”CPU 架构上使用普通编译器进行调试构建。现在它是正确的,IMO,并给出了正确的上下文来很好地解释效果的具体细节,a
可以在&&
条件的每个步骤之间变化。以上是关于谁能解释一下((a == 1 && a == 2 && a == 3)== true)?的主要内容,如果未能解决你的问题,请参考以下文章