易失性和多线程:以下线程安全吗?

发布于 2024-11-18 10:36:53 字数 411 浏览 3 评论 0原文

假设有两个线程分别运行 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

喜爱纠缠 2024-11-25 10:36:53

可能是线程安全的。

线程安全往往取决于上下文。如果您从未读取布尔值,则更新布尔值始终是线程安全的。
如果您确实阅读了其中的内容,那么答案取决于您何时阅读以及所阅读的内容意味着什么。

在某些(但不是全部)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 adding volatile 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 flag volatile, 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 two volatile accesses, but merely a volatile 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.

花桑 2024-11-25 10:36:53

它不是线程安全的。

例如,如果线程在具有单独缓存的 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

魔法唧唧 2024-11-25 10:36:53

首先,易失性用于禁用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.

自由如风 2024-11-25 10:36:53

它是安全的,因为一个线程只能读取,一个线程只能写入。

线程并没有真正共享该标志,一个正在读取,一个正在写入。您不能进行竞争,因为另一个线程永远不会写入错误的结果,并且读取线程永远不会读取错误的结果。简单的。

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.

娇柔作态 2024-11-25 10:36:53

不,事实并非如此。如果值访问是原子的,它可能是线程安全的,但在 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.

梦在深巷 2024-11-25 10:36:53

仍然不安全。您应该使用同步来访问 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.

宣告ˉ结束 2024-11-25 10:36:53

我相信这段代码是安全的,直到两个线程都没有写入 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).

月依秋水 2024-11-25 10:36:53

假设 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.

梦过后 2024-11-25 10:36:53

这段代码似乎不是线程安全的。原因很容易解释。

问题出在下面的代码行

“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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文