C++ 当另一个线程可能设置它(最多一次)时,可以在不锁定它的情况下读取共享布尔标志吗?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/9200951/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Is it ok to read a shared boolean flag without locking it when another thread may set it (at most once)?
提问by John
I would like my thread to shut down more gracefully so I am trying to implement a simple signalling mechanism. I don't think I want a fully event-driven thread so I have a worker with a method to graceully stop it using a critical section Monitor
(equivalent to a C# lock
I believe):
我希望我的线程更优雅地关闭,所以我试图实现一个简单的信号机制。我不认为我想要一个完全事件驱动的线程,所以我有一个工人有一种方法可以使用临界区Monitor
(相当于lock
我相信的 C# )优雅地停止它:
DrawingThread.h
绘图线程.h
class DrawingThread {
bool stopRequested;
Runtime::Monitor CSMonitor;
CPInfo *pPInfo;
//More..
}
DrawingThread.cpp
绘图线程.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();
}
I understand (at least in Windows) Monitor
/lock
s are the least expensive thread synchronization primitive but I am keen to avoid overuse. Should I be wrapping each read of this boolean flag? It is initialized to false and only set once to true when stop is requested (if it is requested before the task completes).
我知道(至少在 Windows 中)Monitor
/ lock
s 是最便宜的线程同步原语,但我很想避免过度使用。我应该包装这个布尔标志的每次读取吗?它被初始化为 false 并且仅在请求停止时设置一次为 true(如果在任务完成之前被请求)。
My tutors advised to protect even bool
's because read/writing may not be atomic. I think this one shot flag is the exception that proves the rule?
我的导师建议保护 even bool
,因为读/写可能不是原子的。我认为这个一杆旗是证明规则的例外?
采纳答案by Dietmar Kühl
It is never OK to read something possibly modified in a different thread without synchronization. What level of synchronization is needed depends on what you are actually reading. For primitive types, you should have a look at atomic reads, e.g. in the form of std::atomic<bool>
.
在没有同步的情况下读取可能在不同线程中修改的内容永远是不可能的。需要什么级别的同步取决于您实际阅读的内容。对于原始类型,您应该查看原子读取,例如以std::atomic<bool>
.
The reason synchronization is always needed is that the processors will have the data possibly shared in a cache line. It has no reason to update this value to a value possibly changed in a different thread if there is no synchronization. Worse, yet, if there is no synchronization it may write the wrong value if something stored close to the value is changed and synchronized.
始终需要同步的原因是处理器可能会在高速缓存行中共享数据。如果没有同步,则没有理由将此值更新为可能在不同线程中更改的值。更糟糕的是,如果没有同步,它可能会写入错误的值,如果存储在值附近的内容被更改和同步。
回答by Tudor
Boolean assignment is atomic. That's not the problem.
布尔赋值是原子的。那不是问题。
The problem is that a thread may not not see changes to a variable done by a different thread due to either compiler or CPU instruction reordering or data caching (i.e. the thread that reads the boolean flag may read a cached value, instead of the actual updated value).
问题是线程可能看不到由于编译器或 CPU 指令重新排序或数据缓存而由不同线程完成的变量更改(即读取布尔标志的线程可能读取缓存值,而不是实际更新的值)价值)。
The solution is a memory fence, which indeed is implicitly added by lock statements, but for a single variable it's overkill. Just declare it as std::atomic<bool>
.
解决方案是一个内存栅栏,它确实是由 lock 语句隐式添加的,但对于单个变量来说它是矫枉过正的。只需将其声明为std::atomic<bool>
.
回答by Max Lybbert
The answer, I believe, is "it depends." If you're using C++03, threading isn't defined in the Standard, and you'll have to read what your compiler and your thread library say, although this kind of thing is usually called a "benign race" and is usuallyOK.
我相信,答案是“视情况而定”。如果您使用的是 C++03,则标准中未定义线程,您必须阅读编译器和线程库所说的内容,尽管这种事情通常被称为“良性竞赛”并且是通常可以。
If you're using C++11, benign races are undefined behavior. Even when undefined behavior doesn't make sense for the underlying data type. The problem is that compilers can assume that programs have no undefined behavior, and make optimizations based on that(see also the Part 1 and Part 2 linked from there). For instance, your compiler could decide to read the flag once and cache the value because it's undefined behavior to write to the variable in another thread without some kind of mutex or memory barrier.
如果您使用的是 C++11,良性竞争是未定义的行为。即使未定义的行为对基础数据类型没有意义。问题是编译器可以假设程序没有未定义的行为,并基于此进行优化(另请参阅从那里链接的第 1 部分和第 2 部分)。例如,您的编译器可能决定读取一次标志并缓存该值,因为在没有某种互斥锁或内存屏障的情况下写入另一个线程中的变量是未定义的行为。
Of course, it may well be that yourcompiler promises to not make that optimization. You'll need to look.
当然,很可能您的编译器承诺不会进行这种优化。你需要看看。
The easiest solution is to use std::atomic<bool>
in C++11, or something like Hans Boehm's atomic_opselsewhere.
最简单的解决方案是std::atomic<bool>
在 C++11 中使用,或者在其他地方使用Hans Boehm 的 atomic_ops 之类的东西。
回答by J?rg Beyer
No, you have to protect every access, since modern compilers and cpus reorder the code without your multithreading tasks in mind. The read access from different threads might work, but don't have to work.
不,你必须保护每一次访问,因为现代编译器和 CPU 重新排序代码而不考虑你的多线程任务。来自不同线程的读取访问可能有效,但不一定有效。