安全使用volatile bool强制另一个线程等待?(C++)

Mat*_*out 8 c++ multithreading boolean volatile thread-safety

我读过的关于volatile的一切都说它永远不会安全,但我仍然倾向于尝试它,而且我还没有看到这个特定场景被宣布为不安全.

我有一个单独的线程渲染场景,从主模拟线程中提取数据.这没有同步,并且工作正常.

问题是当程序退出时,渲染器需要停止从模拟线程中提取数据,然后模拟线程才能安全地自我清理,而不会导致渲染器尝试读取无效内存.

为了实现这一点,我让渲染器在其线程中无限运行:

volatile bool stillRendering;

void RenderThreadFunction()
{
    stillRendering = true;

    while(programRunning)
    {
        renderer->render();
    }

    stillRendering = false;
}
Run Code Online (Sandbox Code Playgroud)

在主程序线程中,当收到windproc退出消息时,我会:

void OnQuit()
{
    programRunning = false;
    while(stillRendering)
    {
    }

    delete application;
}
Run Code Online (Sandbox Code Playgroud)

这样做的目的是确保渲染器在应用程序上调用delete之前停止从应用程序中提取数据.

我首先尝试了这个没有任何volatile关键字,并且它在调试模式下工作,但在发布模式下它挂了.我假设编译器进行了一些优化,导致程序停止检查stillRendering的值.

将volatile添加到stillRendering会导致应用程序在我到目前为止每次测试时成功退出.我不确定为什么"programRunning"不稳定似乎并不重要.

最后,我不确定如何使用volatile为"stillRendering"影响程序的性能.如果使用StillRendering volatile会影响OnQuit()的性能,那对我来说并不重要,但如果它影响RenderThreadFunction()的性能,它对我来说很重要

Jam*_*nze 8

虽然它可能适用于某些编译器,但它完全不安全.基本上,volatile只影响它所附加的变量,因此RendererThreadFunction,例如,完成 之前可以设置为 stillRenderingfalse .(这是正确的,即使两者 并均不稳定.)问题的probablility是非常小的,因此测试可能不会透露它.最后,VC++的一些版本给 的原子访问的语义在C++ 11,在这种情况下,你的代码将工作.(当然,在使用不同版本的VC++编译之前.)renderer->render();stillRenderingprogramRunningvolatile

鉴于renderer->render()几乎肯定会花费不可忽略的时间,因此绝对没有理由不在此处使用条件变量.关于你volatile用于此类事情的唯一时间是关闭机制是否由信号触发(在这种情况下,类型将是sig_atomic_t,而不是bool,虽然在实践中,它可能没有任何区别).在这种情况下,不会有两个线程,只有渲染器线程和信号处理程序.


Cor*_*son 6

如果您希望您的代码适用于所有编译器中的所有体系结构,请使用C++ 11 atomics:

std::atomic<bool> stillRendering;

void RenderThreadFunction()
{
    stillRendering = true;

    while(programRunning)
    {
        renderer->render();
    }

    stillRendering = false;
}
Run Code Online (Sandbox Code Playgroud)

Volatile 不适用于多线程 - 标准实际上允许编译器volatile使用非volatile访问重新排序访问.VC++扩展了它volatile的功能集以防止重新排序,但是其他编译器没有,并且它可能会破坏那些编译器.

正如其他人所提到的那样,volatile也不会影响可见性,这意味着非缓存一致的体系结构可能永远不会看到标志集.x86甚至不会立即缓存一致(写入速度非常慢),因此当通过各种缓冲区发送写入时,程序将总是以超出应有的速度循环.

C++ 11原子避免了这两个问题.

好的,所以这主要是为了纠正你当前的代码并警告你不要滥用volatile.詹姆斯建议使用条件变量(这只是你正在做的更有效的版本)可能是最适合你的实际解决方案.

  • @MattSwarthout你完全理解他.只要循环中没有`volatile`访问或IO,编译器就可以合法地将`stillRendering = false`移动到循环之前.(实际上,没有人这样做,但他们通常不会发出任何保证硬件不会进行此类重新排序的指令.) (2认同)
  • 确实.`volatile`只能阻止与其他挥发物重新排序_. (2认同)