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

时间:2011-07-06 06:17:11

标签: c++ multithreading volatile

假设有两个线程分别运行Thread1()Thread2()。线程1只设置一个全局标志来告诉线程2退出,线程2定期检查它是否应该退出。

volatile bool is_terminate = false;

void Thread1()
{
    is_terminate = true;
}

void Thread2()
{
    while (!is_terminate) {
        // ...
    }
}

我想问一下上述代码是否安全,假设对is_terminate的访问是原子的。我已经知道许多材料表明volatile通常无法确保线程安全。但是在只共享一个原子变量的情况下,我们真的需要使用锁来保护共享变量吗?

9 个答案:

答案 0 :(得分:17)

可能是类型的线程安全。

线程安全往往取决于上下文。如果您从未读取,则更新bool 始终线程安全。 如果您确实从中读取,那么答案取决于何时从中读取,以及读取的含义。

在某些CPU上,但不是全部,对bool类型的对象的写入将是原子的。 x86 CPU通常会使其成为原子,但其他人可能不会。如果更新不是原子的,那么添加volatile对您没有帮助。

但下一个问题是重新排序。编译器(和CPU)将按指定的顺序对volatile变量执行读/写操作,而不进行任何重新排序。这很好。

但它无法保证相对于所有非volatile内存访问重新排序一个volatile内存访问权限。所以一个常见的例子就是你定义了某种标志以保护对资源的访问,你创建了标志volatile,然后编译器移动资源访问,以便在之前发生检查国旗。允许这样做,因为它没有重新排序两个volatile访问的内部排序,而只是volatile和非volatile访问。

老实说,我问的问题是为什么不正确地做到volatile可能会在这种情况下起作用,但为什么不为自己省去麻烦,更清楚地说明它是正确的?在它周围拍打一个记忆障碍。

答案 1 :(得分:11)

它不是线程安全的。

例如,如果线程在具有单独高速缓存的CPU上运行,则没有语言规则表明在编写volatile变量时要同步高速缓存。如果有的话,另一个线程可能在很长一段时间内都看不到变化。


以另一种方式回答:

如果volatile足以保证线程安全,为什么C ++ 0x会用原子操作添加整章?

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2047.html

答案 2 :(得分:4)

首先, volatile 用于在c / c ++中禁用编译优化。请参阅this了解volatile。

原子的核心是单词对齐和 is_terminate 的大小,如果 is_terminate 的大小小于机器本机大小并且对齐,那么它的R和W是原子的

在你的上下文中,有或没有volatile,thread2可能在thread1修改后读取旧值,但是thread2最终可以读取它。

如果最终读取对您没问题,那么您的代码就是线程安全。

答案 3 :(得分:2)

这是安全的,因为一个线程只是在阅读而一个只是在写作。

线程并没有真正共享那个标志,一个正在阅读,一个正在写。你不能参加比赛,因为另一个线程永远不会写出错误的结果,而阅读线程永远不会读取错误的结果。简单。

答案 4 :(得分:1)

不,不是。它可以是线程安全的 如果值访问是原子的 ,但在C ++中你不能假设变量访问是线程安全的,除非你使用一些特定于编译器的结构或同步原语。

答案 5 :(得分:1)

仍然不安全。您应该使用synchronizaton来访问is_terminate不能保证对bool的访问是原子操作。

答案 6 :(得分:1)

我相信这段代码是安全的,直到两个线程都没有写bool(你已经提到过值访问是原子)。

答案 7 :(得分:1)

假设volatile关键字强加任何类型的线程安全性的一个大问题是C或C ++标准在它们描述的抽象机器中没有线程概念。

标准对volatile关键字施加的保证仅在线程内有效 - 而不是在多个线程之间。

这使得实现者可以完全自由地在线程上做任何他们喜欢的事情。如果他们选择将volatile关键字实现为跨线程安全,那么您很幸运。但通常情况并非如此。

答案 8 :(得分:-1)

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

问题出在下面的代码行

“is_terminate = true;”

即使对“is_terminate”的访问是原子的,上面的语句也不是原子的。 该声明包含多个操作。像加载“is_terminate”并更新“is_terminate”。

现在问题是如果is_terminate已加载但未更新,并且线程切换到另一个。 现在线程2的预期结果为真,但它不会得到它。

确保“is_terminate = true;”是原子的。所以锁定它。

希望它有所帮助。