8

是否允许兼容 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 之前的版本中是否允许这种优化?

4

2 回答 2

13

绝对地。

由于x没有标记为volatile并且看起来是一个具有自动存储时长和内部链接的本地对象,并且程序没有对其进行修改,因此这两个程序是等价的。

在 C++03 和 C++11 中,这是 as-if 规则,因为访问非易失性对象被视为程序的“副作用”:

[C++11: 1.9/12]: 访问由 volatile glvalue (3.10) 指定的对象、修改对象、调用库 I/O 函数或调用执行任何这些操作的函数都是副作用,它们是执行环境状态的变化。表达式(或子表达式)的评估通常包括值计算(包括确定对象的身份以进行左值评估和获取先前分配给对象以进行纯右值评估)和副作用的启动。当对库 I/O 函数的调用返回或对 volatile 对象的访问被评估时,副作用被认为是完整的,即使调用(例如 I/O 本身)或 volatile 访问隐含的一些外部操作可能还没有完成。

C++11 确实为全局对象腾出了空间,可以在一个线程中更改其值,然后在另一个线程中读取新值:

[C++11: 1.10/3]:T根据以下规则,在特定点对线程可见的对象的值是对象的初始值、分配给对象T值或由另一个线程分配给对象的值

但是,如果您这样做,因为您的对象不是原子的:

[C++11: 1.10/21]:如果程序的执行包含不同线程中的两个冲突操作,则该程序的执行包含数据竞争,其中至少一个不是原子的,并且两者都不会在另一个之前发生。任何此类数据竞争都会导致未定义的行为。

而且,当调用未定义的行为时,任何事情都可能发生

引导笔记

[C++11: 1.10/25]:实现应确保由原子或同步操作分配的最后一个值(按修改顺序)将在有限的时间段内对所有其他线程可见。

再次注意,对象必须是原子的(例如std::atomic<bool>)才能获得此保证。

于 2013-03-03T15:58:10.250 回答
1

允许编译器对这两个循环做任何事情。包括终止程序。因为如果无限循环不执行类似同步的操作(执行需要与另一个线程或 I/O 同步的操作),无限循环具有未定义的行为,根据C++ 内存模型

请注意,这意味着具有无限递归或无限循环的程序(无论是作为 for 语句实现还是通过循环 goto 或其他方式实现)具有未定义的行为。

于 2017-12-04T21:31:13.223 回答