需要验证一个思维过程,比如说我建立了一个线程如下
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
缓存在每个处理器(或线程)缓存中,并且写入另一个线程的事实在未知的一段时间内(可能永远)不会反射(reflect)在您的工作线程中。 。更进一步,聪明的编译器可能会注意到您正在读取
threadRun
而不进行同步。然后,它可以用来证明没有同步的任何人都无法在另一个线程中写入threadRun
。一旦证明了这一点,它也可以消除对互斥对象中
threadRun
的检查。不要执行未定义的行为,除非您要验证从现在开始直到每次编译的程序结束时所产生的程序集,即使该程序“有效”,或者 yield 巨大且值得冒险。
用
threadRun
替换std::atomic_bool
。在最坏的情况下,您将失去一点性能并获得正确性。