易失性和多线程:以下线程安全吗?
假设有两个线程分别运行 Thread1()
和 Thread2()
。线程 1 只是设置一个全局标志来告诉线程 2 退出,线程 2 定期检查是否应该退出。
volatile bool is_terminate = false;
void Thread1()
{
is_terminate = true;
}
void Thread2()
{
while (!is_terminate) {
// ...
}
}
我想问一下,假设对 is_terminate
的访问是原子的,上面的代码是否安全。我已经知道许多材料表明 易失性通常不能确保线程安全。但是在只有一个原子变量被共享的情况下,我们真的需要使用锁来保护共享变量吗?
Assume there are two threads running Thread1()
and Thread2()
respectively. The thread 1 just sets a global flag to tell thread 2 to quit and thread 2 periodically checks if it should quit.
volatile bool is_terminate = false;
void Thread1()
{
is_terminate = true;
}
void Thread2()
{
while (!is_terminate) {
// ...
}
}
I want to ask if the above code is safe assuming that access to is_terminate
is atomic. I already know many materials state that volatile
can not insure thread-safety generally. But in the situation that only one atomic variable is shared, do we really need to protect the shared variable using a lock?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(9)
它可能是线程安全的。
线程安全往往取决于上下文。如果您从未读取布尔值,则更新布尔值始终是线程安全的。
如果您确实阅读了其中的内容,那么答案取决于您何时阅读以及所阅读的内容意味着什么。
在某些(但不是全部)CPU 上,对
bool
类型的对象的写入将是原子的。 x86 CPU 通常会将其原子化,但其他 CPU 可能不会。如果更新不是原子的,那么添加易失性
对您没有帮助。但下一个问题是重新排序。编译器(和CPU)将按照指定的顺序对
易失性
变量进行读/写,而不进行任何重新排序。所以这样很好。但它不能保证相对于所有非易失性内存访问重新排序一个易失性内存访问。因此,一个常见的例子是,您定义某种标志来保护对资源的访问,您将标志设置为
易失性
,然后编译器将资源访问向上移动,以便它发生在之前 你检查标志。允许这样做,因为它不会重新排序两个易失性
访问的内部顺序,而只是一个易失性
和一个非易失性
访问。老实说,我要问的问题是为什么不正确地做?
在这种情况下,
易失性
可能会起作用,但为什么不省去自己的麻烦,并更清楚地表明它是正确的呢?相反,在它周围设置一个内存屏障。It is probably sort of thread-safe.
Thread safety tends to depend on context. Updating a bool is always thread safe, if you never read from it.
And if you do read from it, then the answer depends on when you read from it, and what that read signifies.
On some CPUs, but not all, writes to an object of type
bool
will be atomic. x86 CPUs will generally make it atomic, but others might not. If the update isn't atomic, then addingvolatile
won't help you.But the next problem is reordering. The compiler (and CPU) will carry out reads/writes to
volatile
variables in the order specified, without any reordering. So that's good.But it makes no guarantee about reordering one
volatile
memory access relative to all the non-volatile
ones. So a common example is that you define some kind of flag to protect access to a resource, you make the flagvolatile
, and then the compiler moves the resource access up so it happens before you check the flag. It's allowed to do that, because it's not reordering the internal ordering of twovolatile
accesses, but merely avolatile
and a non-volatile
one.Honestly, the question I'd ask is why not just do it properly?
It is possible that
volatile
will work in this situation, but why not save yourself the trouble, and make it clearer that it's correct? Slap a memory barrier around it instead.它不是线程安全的。
例如,如果线程在具有单独缓存的 CPU 上运行,则没有语言规则规定在写入 易失性变量时要同步缓存。另一个线程可能在很长一段时间内看不到更改(如果有的话)。
换句话说:
如果 volatile 足以保证线程安全,为什么 C++0x 添加一整章的原子操作?
http://www.open-std.org /jtc1/sc22/wg21/docs/papers/2006/n2047.html
It is not thread safe.
If the threads, for example, are run on CPUs with separate caches there are no language rules saying that the caches are to be synchronized when writing a volatile variable. The other thread may not see the change for a very long time, if ever.
To answer in another way:
If
volatile
is enough to be thread safe, why is C++0x adding an entire chapter with atomic operations?http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2047.html
首先,易失性用于禁用c/c++中的编译优化。请参阅此了解易失性。
原子的核心是字对齐和is_terminate的大小,如果is_terminate的大小小于机器原生大小并且对齐,那么它的R和W就是原子的。
在您的上下文中,无论有或没有易失性,线程2都可能在线程1修改后读取旧值,但线程2最终可以读取它。
如果最终读取对您来说没问题,那么您的代码就是线程安全的。
First, volatile is used for disabling compile optimization in c/c++. see this for understanding volatile.
The core of atomic is word align and size of is_terminate, if size of is_terminate is less than machine native size and aligned, then R and W of it is atomic.
In your context, with or without volatile, thread2 may read old value after thread1 modified it, but thread2 can read it eventually.
If eventually-read is OK for you, then your codes are thread safety.
它是安全的,因为一个线程只能读取,一个线程只能写入。
线程并没有真正共享该标志,一个正在读取,一个正在写入。您不能进行竞争,因为另一个线程永远不会写入错误的结果,并且读取线程永远不会读取错误的结果。简单的。
it's safe because one thread is only reading and one is only writing.
The threads aren't really sharing that flag, one is reading, one is writing. You can't have a race because the other thread will never write a bad result, and the reading thread will never read a bad result. simple.
不,事实并非如此。如果值访问是原子的,它可能是线程安全的,但在 C++ 中,你不能假设变量访问是线程安全的,除非你使用一些特定于编译器的构造或同步原语。
No, it is not. It could be thread safe if the value access was atomic, but in C++ you can't assume that variables access is thread-safe unless you use some compiler-specific constructs or synchronization primitives.
仍然不安全。您应该使用同步来访问
is_terminate
对 bool 的访问不保证是原子操作。It is still not safe. You should use synchronizaton to access
is_terminate
Access to the bool is not guaranteed to be an atomic operation.我相信这段代码是安全的,直到两个线程都没有写入
bool
(您已经提到过值访问是原子)。I believe that this code is safe, until both the threads are not writing the
bool
(already you have mentioned that value access is atomic).假设 volatile 关键字强加任何类型的线程安全性,最大的问题是 C 或 C++ 标准在它们描述的抽象机中没有线程的概念。
标准对
volatile
关键字施加的保证仅在线程内有效,而不是在多个线程之间有效。这使得实现者在线程方面可以完全自由地做任何他们想做的事情。如果他们选择实现 volatile 关键字以确保跨线程安全,那么您很幸运。但通常情况并非如此。
The big problem with assuming that the
volatile
keyword imposes any kind of thread safety, is that the C or C++ standards have no concept of threads in the abstract machine they describe.The guarantees that the standard imposes on the
volatile
keyword, are only valid within a thread - not between multiple threads.This leaves implementors with full liberty to do whatever they please when it comes to threads. If they chose to implement the
volatile
keyword to be safe across threads, then you're lucky. More often than not, that's not the case though.这段代码似乎不是线程安全的。原因很容易解释。
问题出在下面的代码行
“is_terminate = true;”
即使对“is_terminate”的访问是原子的,上面的语句也不是原子的。
该语句包含超过 1 个操作。就像加载“is_terminate”和更新“is_terminate”一样。
现在问题是,如果 is_terminate 已加载但未更新,并且线程切换到另一个线程。
现在线程 2 期望结果为 true,但它不会得到它。
确保“is_terminate = true;”是原子的。所以锁定它。
希望有帮助。
This code isn't seems to be thread safe. Reason can be explained easily.
Problem lies in below code line
"is_terminate = true;"
Even if access to "is_terminate" is atomic, above statement is not atomic.
This statement includes more than 1 operations. Like load "is_terminate" and update "is_terminate".
Now gotcha is if is_terminate is loaded and not updated and thread switches to another one.
Now thread 2 expected result to be true but it won't get it.
Make sure "is_terminate = true;" is atomic. So lock it.
Hope it helps.