Rob*_*ger 16 c++ c++11 stdthread stdatomic
我想检查一下是否std::thread
已完成执行.搜索stackoverflow我发现以下问题解决了这个问题.接受的答案建议让工作线程在退出之前设置变量并让主线程检查该变量.以下是此类解决方案的最小工作示例:
#include <unistd.h>
#include <thread>
void work( bool* signal_finished ) {
sleep( 5 );
*signal_finished = true;
}
int main()
{
bool thread_finished = false;
std::thread worker(work, &thread_finished);
while ( !thread_finished ) {
// do some own work until the thread has finished ...
}
worker.join();
}
Run Code Online (Sandbox Code Playgroud)
对已接受的答案发表评论的人声称,不能使用简单的bool
变量作为信号,代码在没有内存屏障的情况下被破坏并且使用std::atomic<bool>
是正确的.我最初的猜测是这是错误的,简单bool
就足够了,但我想确保我没有错过任何东西.以上代码是否需要a std::atomic<bool>
才能正确?
假设主线程和worker正在不同套接字中的不同CPU上运行.我认为会发生的是,主线程thread_finished
从其CPU的缓存中读取.当worker更新它时,缓存一致性协议负责将worker更改为全局内存并使主线程的CPU缓存无效,因此它必须从全局内存中读取更新的值.缓存一致性的全部要点是不能像上面的代码一样工作吗?
das*_*ght 21
对已接受的答案发表评论的人声称,不能使用简单的bool变量作为信号,代码在没有内存屏障的情况下被破坏,并且使用std :: atomic将是正确的.
评论者是正确的:简单bool
是不够的,因为从将线程非原子写入thread_finished
到true
可以重新排序.
考虑一个线程,它将一个静态变量设置x
为一些非常重要的数字,然后指示它的退出,如下所示:
x = 42;
thread_finished = true;
Run Code Online (Sandbox Code Playgroud)
当主线程看到thread_finished
设置为时true
,它假定工作线程已完成.但是,当您的主线程检查时x
,它可能会发现它设置为错误的数字,因为上面的两个写入已被重新排序.
当然,这仅是用于说明一般问题的简化示例.使用std::atomic
您的thread_finished
变量增加了内存屏障,确保它完成之前,所有的写操作.这解决了无序写入的潜在问题.
另一个问题是可以优化对非易失性变量的读取,所以主线程永远不会注意到thread_finished
标志的变化.
thread_finished
挥发是不会去解决问题; 事实上,volatile不应与线程一起使用 - 它适用于内存映射硬件.
使用原始bool
是不够的.
程序的执行包含数据竞争,如果它在不同的线程中包含两个冲突的动作,其中至少有一个不是原子的,并且都不会在另一个之前发生.任何此类数据争用都会导致未定义的行为.§1.10p21
如果其中一个修改内存位置(1.7)而另一个访问或修改相同的内存位置,则两个表达式评估会发生冲突.§1.10p4
你的程序包含一个数据竞争,其中工作线程写入bool并且主线程从中读取,但是操作之间没有正式发生的关系.
有许多不同的方法可以避免数据竞争,包括使用std::atomic<bool>
适当的内存排序,使用内存屏障,或用条件变量替换bool.
归档时间: |
|
查看次数: |
3072 次 |
最近记录: |