c++ - 互斥量和变量更新
问题描述
需要验证一个思维过程,比如说我设置了一个线程如下
bool threadRun=true;
std::mutex threadMutex;
std::condition_variable event;
std::thread processThread(process);
void process()
{
while(threadRun)
{
if(noWork())
{
std::unique_lock<std::mutex> lock(threadMutex);
if(threadRun)
{
if(!getWork()) //try to get work, return false if no work
event.wait(lock);
}
continue;
}
doWork();
}
}
void stopThread()
{
{
std::unique_lock<std::mutex> lock(threadMutex);
threadRun=false;
}
event.notify_all();
processThread.join();
}
由于 threadRun 不在线程中的互斥锁下,因此无法保证调用 stopThread 会真正停止线程,因为当有待办事项时不需要缓存刷新。但是,当没有工作并且锁定被占用时,这应该会导致缓存刷新和 threadRun 保证被更新,对吗?
解决方案
C++ 中的数据竞争是未定义的行为。
现在,您可能会认为这意味着“好吧,我可以接受他们错过阅读,他们最终会明白的”。但是 UB 是编译器可以自由假设不会发生的事情。
您在没有锁或其他同步的情况下阅读的事实threadRun
意味着编译器可以、可能并且应该假设任何其他线程中的任何人都不会修改threadRun
.
因此,代码可以优化为:
if(threadRun)
while(true)
即,编译器可以读取threadRun
一次,然后如果它可以证明没有人可以以定义的方式更改其值,则无需再阅读它。
现在,如果编译器无法证明这一点,它就必须读取threadRun
. 所以你的 UB 表现得更像它“应该”工作。在某些平台上,硬件本身已缓存threadRun
在每个处理器(或线程)缓存中,并且另一个线程写入它的事实不会在某个未知时间段(可能永远)内反映在您的工作线程中。
更进一步,智能编译器可能会注意到您正在阅读threadRun
而不同步。然后它可以用它来证明没有人,无论有没有同步,都可以threadRun
在另一个线程中写入。
一旦证明了这一点,它也可以消除threadRun
互斥体内部的检查。
不要做未定义的行为,除非您想在每次编译时验证程序从现在到结束时生成的程序集,即使它“有效”,或者收益巨大且值得冒险。
替换threadRun
为std::atomic_bool
。在最坏的情况下,您会损失一点点性能并获得正确性。
推荐阅读
- spring - 使用 Jsoup connect 连接到 jenkins
- unity3d - RTX 递归渲染
- reactjs - 将 Fetch 与 React 组件一起使用
- c# - 优化霍夫曼表符号检索(顺序和渐进 JPEG)
- postgresql - 如何将 tokio_postgres 与 Warp 一起使用?
- google-apps-script - 如何颠倒拼接的顺序?
- javascript - 在 HTML 中计算引号内的文本的理想方法是什么?
- javascript - 在没有 getElementById 的情况下通过其 ID(作为 var)处理元素是否合适?
- javascript - 在 React 中,在嵌套节点和不同分支中的兄弟节点之间传递数据的首选设计模式是什么?
- memory-leaks - NVIDIA h.264 编码器 MFT 是否泄漏资源?