允许从C ++ 11兼容的编译器优化/转换此代码:
bool x = true; // *not* an atomic type, but suppose bool can be read/written atomically
/*...*/
{
while (x); // spins until another thread changes the value of x
}
等同于无限循环的任何东西:
{
while (true); // infinite loop
}
从单线程程序的角度来看,上述转换肯定是有效的,但这不是一般情况。
另外,在C ++ 11之前是否允许优化?
答案 0 :(得分:13)
<强>绝对强>
由于x
未标记为volatile
,并且似乎是具有自动存储持续时间和内部链接的本地对象,并且程序未对其进行修改,因此这两个程序是等效的。
在C ++ 03和C ++ 11中,这是按照as-if规则,因为访问非易失性对象不被认为是程序的“副作用” :
[C++11: 1.9/12]:
访问由volatile glvalue(3.10)指定的对象,修改对象,调用库I / O函数或调用执行任何这些操作的函数都是副作用,这是执行环境状态的变化。表达式(或子表达式)的评估通常包括 两个值计算(包括确定glvalue评估对象的身份并获取先前分配给对象以进行prvalue评估的值)和启动副作用。当对库I / O函数的调用返回或对volatile对象的访问进行评估时,即使调用隐含的某些外部操作(例如I / O本身)或易失性访问,也会认为副作用已完成可能尚未完成。
C ++ 11确实为全局对象腾出空间,使其在一个线程中更改其值,然后在另一个线程中读取新值:
[C++11: 1.10/3]:
特定点上线程T
可见的对象的值是对象的初始值,T
指定给对象的值,或根据以下规则由另一个线程分配给该对象的值。
但是,如果你这样做,因为你的对象不是原子的:
[C++11: 1.10/21]:
程序的执行包含数据竞争,如果它在不同的线程中包含两个冲突的动作,其中至少有一个不是原子的,并且都不会发生在另一个之前。任何此类数据竞争都会导致未定义的行为。
并且,当调用未定义的行为时,可能发生任何事情。
[C++11: 1.10/25]:
实现应该确保原子或同步操作分配的最后一个值(按修改顺序)在有限的时间内对所有其他线程可见。
再次注意,该对象必须是 atomic (例如,std::atomic<bool>
)才能获得此保证。
答案 1 :(得分:1)
允许编译器对这两个循环做任何事情。包括终止程序。因为无限循环具有未定义的行为,如果它们不执行类似同步的操作(执行需要与另一个线程或I / O同步的事情),根据C++ memory model:
请注意,这意味着具有无限递归或无限循环的程序(无论是作为for语句实现还是通过循环goto或其他方式)具有未定义的行为。