【问题标题】:Undefined behaviour when using a non-atomic bool使用非原子布尔时的未定义行为
【发布时间】:2014-02-20 01:38:09
【问题描述】:

当使用一个普通的布尔标志来控制一个线程何时停止它正在做的事情时,可能发生的最糟糕的事情是什么?特殊之处在于,线程停止的确切时间根本不是很重要,它只是播放一些媒体,就我所关心的而言,它甚至可能迟到半秒。它有一个简单的 while (!restart) 循环:

while (!restart) //bool restart
{ 
    //do something
}

另一个线程更改了一些设置,然后将 restart 设置为 true:

someSetting = newSetting;
restart = 1;

由于播放循环每秒运行数千次,我担心使用 atomic bool 可能会增加延迟。我知道这是“未定义的行为”,但它是如何表现出来的呢?如果 bool 在某个时候是 54r*wx]%,那又如何呢?我可以得到运行时错误吗?布尔值最终会变为可理解的值,不是吗? (顺便说一句,代码目前有效。)在另一篇文章中,有人建议标志可能永远不会改变,因为线程有单独的缓存 - 这对我来说听起来很可疑,编译器肯定必须确保共享变量被更改,即使存在数据竞赛吗?或者控制线程的执行顺序可能会改变,并且 someSetting 可能会在重启后改变?同样,这听起来令人毛骨悚然,为什么编译器会允许这种情况发生?

我考虑过在循环内设置一个计数器,并且每隔一千次检查一次原子布尔标志。但我不想这样做,除非我真的必须这样做。

【问题讨论】:

  • 可能发生的最糟糕的事情是编译器将其转换为无限循环,或完全消除循环体。
  • 目前循环(和标志)工作正常,因此除非将来更改优化,否则这似乎不是问题。
  • “布尔值最终会变成一个可理解的值,不是吗?”在当今流行的处理器上,是的。但不能保证这种情况会无限期地持续下去——硬件级缓存一致性不会随着处理器数量的增加而很好地扩展。
  • “即使存在数据竞争,编译器也必须确保更改共享变量?” - 不。如果存在数据竞争,则行为未定义。编译器知道它是“共享”的唯一方法是,如果你告诉它,通过使其成为原子或以其他方式同步访问它。
  • @bazza 仅当您在 Visual Studio 上进行编译时。那里没问题,因为 volatile introduces memory fences 在 Visual C++ 上作为非标准扩展。这意味着它们具有与原子标志完全相同的性能影响(因为它们实际上完全一样)。

标签: c++ windows multithreading visual-studio-2013


【解决方案1】:

UB 并不意味着你的代码不起作用,它只是意味着你的代码的行为没有被标准指定。您必须使用 std::atomic 使您的代码符合标准,而无需实际更改行为。您可以使用memory_order_relaxed

atomic<int> restart ....

while (!restart.load(memory_order_relaxed))
{ 
    //do something
}

在另一个线程中:

someSetting = newSetting;
restart.store(1, memory_order_relaxed);

此代码将发出与您的相同的指令。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-04-28
    • 2010-11-20
    • 1970-01-01
    • 1970-01-01
    • 2021-12-27
    • 2018-09-21
    • 2016-03-08
    相关资源
    最近更新 更多