|
1
223
问题在于
但是,我们必须为
剩下的
属性还提供
对于共享数据的线程安全访问,我们需要保证:
防止重新排序的解决方案是使用 记忆障碍 不能跨此点重新排序内存访问 . 在易失性变量访问周围设置这样的屏障,可以确保即使是非易失性访问也不会跨易失性变量重新排序,从而允许我们编写线程安全的代码。
然而,记忆障碍
自从C++11以来,原子变量(
|
|
|
2
52
你也可以从 Linux Kernel Documentation .
|
|
|
3
12
混乱是因为volatile不足以实现许多事情。特别是,现代系统使用多级缓存,现代多核cpu在运行时进行一些奇特的优化,现代编译器在编译时进行一些奇特的优化,这些都会导致各种副作用以不同的顺序出现,而这些顺序与您只看源代码时所期望的顺序不同。
就我个人而言,volatile标志的主要用途是“plessegoawaynow”布尔值。如果我有一个连续循环的工作线程,我会让它在循环的每次迭代中检查volatile boolean,如果boolean为真就退出。然后,主线程可以安全地清理工作线程,方法是将布尔值设置为true,然后调用pthread\u join()等待工作线程消失。 |
|
4
9
多线程编程的典型方法不是在机器级保护每个共享变量,而是引入引导程序流的保护变量。而不是
这不仅封装了“硬部分”,而且从根本上说是必要的:C不包括
原子操作
实现互斥所必需的;它只有
现在你有了这样的东西:
|
|
|
5
7
你的理解真的错了。 volatile变量的属性是“对这个变量的读写是程序可感知行为的一部分”。这意味着该程序可以工作(给定适当的硬件):
例如,线程安全计数器应该是(类似于linux内核的代码,不知道c++0x等价物):
这是原子的,没有记忆障碍。如果需要的话,你应该添加它们。添加volatile可能不会有帮助,因为它不会关联到对附近代码的访问(例如,将元素附加到计数器正在计数的列表中)。当然,您不需要看到计数器在您的程序之外递增,而且优化仍然是可取的,例如。
如果优化器足够聪明(它不会改变代码的语义)。 |
|
|
6
6
1) 原子性,也就是说,如果我读或写一些数据到内存中,那么这些数据在一个过程中被读/写,并且不能由于上下文切换而被中断或争用 看到 在多个并发环境之间是相同的,比如线程、机器等 无论是上述的还是更特别的,无论是C还是C++标准,对于挥发性如何表现都不包括上述两种。 在实践中更糟糕的是,有些编译器(如英特尔安腾编译器)确实试图实现某些并发访问安全行为元素(即通过确保内存限制),但是编译器实现之间没有一致性,而且标准一开始并不要求实现这一点。 将变量标记为volatile只意味着每次都要强制将值刷新到内存中或从内存中刷新,这在许多情况下会降低代码的速度,因为基本上已经破坏了缓存性能。 c#和javaafaik通过使volatile遵守1)和2)来纠正这一点,但是对于c/c++编译器来说,情况并非如此,因此基本上可以根据您的需要使用volatile。 关于这个问题的更深入的(尽管不是无偏见的)讨论,请阅读 this |
|
|
7
6
comp.programming.threads常见问题解答 a classic explanation 作者:Dave Butenhof:
布滕霍夫先生在这方面也有很多相同的见解 this usenet post :
|
|
|
8
3
这就是“volatile”所做的一切: “嘿,编译器,这个变量可以在任何时刻(在任何时钟滴答声上)改变,即使没有本地指令作用于它。不要在寄存器中缓存此值。“
您可能会遇到像“Dobbs博士”这样的文章,这些文章被认为是多线程编程的灵丹妙药。他的方法并非完全没有优点,但它有一个根本的缺陷,即让对象的用户对其线程安全负责,这往往与其他违反封装的行为有相同的问题。 |
|
|
9
3
有选择有“不稳定”的意思吗 “多进程环境中的线程安全访问” . 但他们没有。
这意味着关键代码段周围的“volatile”信号量之类的东西,在新的硬件和新的编译器上不起作用,可能曾经在旧的硬件上和旧的编译器起作用,而旧的例子有时不是错的,只是旧的。 |
|
|
Fredericson · 如何避免在Java中使用volatile 8 年前 |
|
|
razorozx · C++如何获取父数据类型的sizeof? 8 年前 |
|
|
JavaKaKida · 单核cpu java中的易失性 8 年前 |
|
|
gstackoverflow · 顺序一致性挥发性解释 8 年前 |
|
|
AlastairG · volatile关键字如何影响静态常量数组? 8 年前 |