如何"未定义"竞争条件?

And*_*rew 5 c++ race-condition undefined-behavior

假设我定义了一个以下的C++对象:

class AClass
{
public:
    AClass() : foo(0) {}
    uint32_t getFoo() { return foo; }
    void changeFoo() { foo = 5; }
private:
    uint32_t foo;
} aObject;
Run Code Online (Sandbox Code Playgroud)

该对象由两个线程T1和T2共享.T1经常getFoo()在循环中调用以获得一个数字(如果changeFoo()以前没有被调用,它将始终为0 ).在某些时候,T2调用changeFoo()它来改变它(没有任何线程同步).

是否有任何实际的机会有史以来T1获得的值会有所不同比0或5 的现代计算机体系结构和编译器?到目前为止我调查的所有汇编代码都使用32位内存读写,这似乎可以节省操作的完整性.

其他原始类型呢?

实际意味着您可以提供现有体系结构或符合标准的编译器的示例,其中理论上可以实现此(或具有不同代码的类似情况).我把" 现代"这个词留下了一点主观.


编辑:我可以看到很多人注意到我不应该期待5被读.这对我来说完全没问题,我没有说我这么做(虽然感谢你指出这方面的问题).我的问题更多的是关于上述代码可能发生什么样的数据完整性违规.

Mat*_* M. 10

在实践中,你不会看到任何其他东西,0或者5据我所知(也许是一些奇怪的16位架构,32位int,而事实并非如此).

但是,你是否真的看到5了并不能保证.

假设我是编译器.

我知道了:

while (aObject.getFoo() == 0) {
    printf("Sleeping");
    sleep(1);
}
Run Code Online (Sandbox Code Playgroud)

我知道:

  • printf 不能变 aObject
  • sleep 不能变 aObject
  • getFoo不会改变aObject(谢谢内联定义)

因此我可以安全地转换代码:

while (true) {
    printf("Sleeping");
    sleep(1);
}
Run Code Online (Sandbox Code Playgroud)

因为根据C++标准aObject,在此循环期间没有其他人访问.

这就是未定义的行为意味着什么:炸毁了预期.

  • @EdS.:你可能不喜欢它,但这个答案提出了一个非常重要的观点,因为它解释了这段代码如何无法工作*实际上*.它给出了一个合理的理由,为什么一个真正的编译器可能会破坏这个代码,而不诉诸"鼻子恶魔"或"订购披萨"或"编译器讨厌你而只是想惹你".这个答案指出了为什么优化器可能会破坏代码以使代码快速运行.我认为这非常重要.因为这意味着即使每个编译器今天接受代码,下一个版本也可能失败 (3认同)
  • @EdS.:不,因为你非常想指出,他问了别的*.他假设"哦,我们肯定会得到值0和5",所以他反而问"但我们也会得到*其他*中间值".不,我们(实际上说)不会,但他的前提是有缺陷的,因为我们可能永远也看不到价值5.所以你是对的,从技术上来说,它不会回答这个问题,但它回答了他需要知道的事情.因为导致这个问题的假设是错误的. (3认同)
  • @EdS.:**和**只要两者都没有被优化(因为否则它们根本不会到达CPU,然后它们是否被原子执行并不重要),但正如这个答案指出的那样,优化器可以对其进行优化 (2认同)
  • @KBart"volatile"作为并发上下文中有时适用的同步原语是MS扩展.使用`volatile`作为同步原语是不可移植的或推荐用于现代程序. (2认同)

Mar*_*tos 4

实际上,所有主流 32 位架构都以原子方式执行 32 位读写。除了 0 或 5 之外,你永远不会看到任何东西。

  • @bamboon:我提出了两个主张。其中哪一个是假的? (6认同)
  • +1为实际答案,不基于任何宗教信仰。如果您在*原子*操作中存储/读取变量,则不存在竞争条件,也不存在未定义的行为。 (2认同)

归档时间:

查看次数:

747 次

最近记录:

12 年,9 月 前