Joh*_*ohn 42 c++ multithreading boolean locking monitor
我希望我的线程更优雅地关闭,所以我试图实现一个简单的信令机制.我不认为我想要一个完全事件驱动的线程,所以我有一个工人用一个方法来使用一个关键部分Monitor(相当于一个C#lock我相信)来优雅地停止它:
DrawingThread.h
class DrawingThread {
bool stopRequested;
Runtime::Monitor CSMonitor;
CPInfo *pPInfo;
//More..
}
Run Code Online (Sandbox Code Playgroud)
DrawingThread.cpp
void DrawingThread::Run() {
if (!stopRequested)
//Time consuming call#1
if (!stopRequested) {
CSMonitor.Enter();
pPInfo = new CPInfo(/**/);
//Not time consuming but pPInfo must either be null or constructed.
CSMonitor.Exit();
}
if (!stopRequested) {
pPInfo->foobar(/**/);//Time consuming and can be signalled
}
if (!stopRequested) {
//One more optional but time consuming call.
}
}
void DrawingThread::RequestStop() {
CSMonitor.Enter();
stopRequested = true;
if (pPInfo) pPInfo->RequestStop();
CSMonitor.Exit();
}
Run Code Online (Sandbox Code Playgroud)
我理解(至少在Windows中)Monitor/ locks是最便宜的线程同步原语,但我很想避免过度使用.我应该包装每个读取此布尔标志吗?它被初始化为false,并且只在请求停止时设置为true(如果在任务完成之前请求它).
我的导师建议保护甚至bool是因为读/写可能不是原子的.我认为这一次射击旗是证明规则的例外吗?
Die*_*ühl 45
在没有同步的情况下读取可能在不同线程中修改的内容永远不可能.需要什么级别的同步取决于您实际阅读的内容.对于原始类型,您应该查看原子读取,例如以形式std::atomic<bool>.
始终需要同步的原因是处理器将具有可能在高速缓存行中共享的数据.如果没有同步,则没有理由将此值更新为可能在其他线程中更改的值.更糟糕的是,如果没有同步,如果存储在值附近的内容被更改和同步,则可能会写入错误的值.
Tud*_*dor 12
布尔赋值是原子的.那不是问题.
问题是由于编译器或CPU指令重新排序或数据缓存,线程可能看不到由不同线程完成的变量的更改(即读取布尔标志的线程可能读取缓存值,而不是实际更新值).
解决方案是一个内存栅栏,它实际上是由锁定语句隐式添加的,但是对于单个变量来说它是过度的.只需声明为std::atomic<bool>.
我相信答案是"这取决于".如果你正在使用C++ 03,标准中没有定义线程,你必须阅读你的编译器和你的线程库所说的内容,尽管这种事情通常被称为"良性竞赛" ,并且通常没关系.
如果您使用的是C++ 11,那么良性竞赛是未定义的行为.即使未定义的行为对底层数据类型没有意义.问题是编译器可以假定程序没有未定义的行为,并根据它进行优化(另请参见从那里链接的第1部分和第2部分).例如,您的编译器可能决定一次读取标志并缓存该值,因为它是未定义的行为,在没有某种互斥或内存屏障的情况下写入另一个线程中的变量.
当然,很可能你的编译器承诺不进行优化.你需要看看.
最简单的解决方案是std::atomic<bool>在C++ 11中使用,或者在其他地方使用Hans Boehm的atomic_ops.