何时在多线程中使用 volatile?
如果有两个线程访问一个全局变量,那么许多教程都说使变量 volatile 以防止编译器将变量缓存在寄存器中,从而无法正确更新.然而,两个线程都访问一个共享变量是需要通过互斥锁进行保护的东西,不是吗?但是在这种情况下,在线程锁定和释放互斥锁之间,代码处于一个关键部分,只有一个线程可以访问变量,在这种情况下,变量不需要是易失的?
If there are two threads accessing a global variable then many tutorials say make the variable volatile to prevent the compiler caching the variable in a register and it thus not getting updated correctly. However two threads both accessing a shared variable is something which calls for protection via a mutex isn't it? But in that case, between the thread locking and releasing the mutex the code is in a critical section where only that one thread can access the variable, in which case the variable doesn't need to be volatile?
那么,多线程程序中 volatile 的用途/目的是什么?
So therefore what is the use/purpose of volatile in a multi-threaded program?
推荐答案
Short &快速回答:volatile
(几乎)对于与平台无关的多线程应用程序编程毫无用处.它不提供任何同步,不创建内存栅栏,也不确保操作的执行顺序.它不会使操作原子化.它不会使您的代码神奇地线程安全.volatile
可能是所有 C++ 中最容易被误解的工具.请参阅 此,this 和 this 了解有关 volatile 的更多信息
Short & quick answer: volatile
is (nearly) useless for platform-agnostic, multithreaded application programming. It does not provide any synchronization, it does not create memory fences, nor does it ensure the order of execution of operations. It does not make operations atomic. It does not make your code magically thread safe. volatile
may be the single-most misunderstood facility in all of C++. See this, this and this for more information about volatile
另一方面,volatile
确实有一些可能不那么明显的用途.它的使用方式与使用 const
帮助编译器向您显示在以非保护方式访问某些共享资源时可能出错的地方相同.Alexandrescu 在这篇文章中讨论了这种用法.然而,这基本上是以一种通常被视为一种发明的方式使用 C++ 类型系统,并且会引发未定义的行为.
On the other hand, volatile
does have some use that may not be so obvious. It can be used much in the same way one would use const
to help the compiler show you where you might be making a mistake in accessing some shared resource in a non-protected way. This use is discussed by Alexandrescu in this article. However, this is basically using the C++ type system in a way that is often viewed as a contrivance and can evoke Undefined Behavior.
volatile
专门用于与内存映射硬件、信号处理程序和 setjmp 机器代码指令交互时使用.这使得 volatile
直接适用于系统级编程,而不是普通的应用级编程.
volatile
was specifically intended to be used when interfacing with memory-mapped hardware, signal handlers, and the setjmp machine code instruction. This makes volatile
directly applicable to systems-level programming rather than normal applications-level programming.
2003 C++ 标准没有说volatile
对变量应用任何类型的Acquire 或Release 语义.事实上,标准对多线程的所有问题完全保持沉默.但是,特定平台确实对 volatile
变量应用 Acquire 和 Release 语义.
The 2003 C++ Standard does not say that volatile
applies any kind of Acquire or Release semantics on variables. In fact, the Standard is completely silent on all matters of multithreading. However, specific platforms do apply Acquire and Release semantics on volatile
variables.
C++11 标准现在确实在内存模型和语言中直接承认多线程,并且它提供了库设施以独立于平台的方式处理它.然而 volatile
的语义仍然没有改变.volatile
仍然不是同步机制.Bjarne Stroustrup 在 TCPPPL4E 中说了这么多:
The C++11 Standard now does acknowledge multithreading directly in the memory model and the language, and it provides library facilities to deal with it in a platform-independent way. However the semantics of volatile
still have not changed. volatile
is still not a synchronization mechanism. Bjarne Stroustrup says as much in TCPPPL4E:
不要使用 volatile
除非在直接处理的低级代码中与硬件.
Do not use
volatile
except in low-level code that deals directly with hardware.
不要假设 volatile
在内存模型中具有特殊含义.它才不是.它不是――如在一些后来的语言中――同步机制.要获得同步,请使用 atomic
,一个mutex
,或 condition_variable
.
Do not assume volatile
has special meaning in the memory model. It
does not. It is not -- as in some later languages -- a
synchronization mechanism. To get synchronization, use atomic
, a
mutex
, or a condition_variable
.
[/结束更新]
以上所有内容均适用于 C++ 语言本身,由 2003 标准(以及现在的 2011 标准)定义.然而,一些特定平台确实为 volatile
的功能添加了额外的功能或限制.例如,在 MSVC 2010 中(至少)Acquire and Release 语义 do 适用于对 volatile
变量的某些操作.来自 MSDN:
[/End update]
The above all applies to the C++ language itself, as defined by the 2003 Standard (and now the 2011 Standard). Some specific platforms however do add additional functionality or restrictions to what volatile
does. For example, in MSVC 2010 (at least) Acquire and Release semantics do apply to certain operations on volatile
variables. From the MSDN:
在优化时,编译器必须保持引用之间的顺序到 volatile 对象以及对其他全局对象的引用.在特别是,
When optimizing, the compiler must maintain ordering among references to volatile objects as well as references to other global objects. In particular,
对 volatile 对象的写入(volatile write)具有 Release 语义;一个对在写入之前发生的全局或静态对象的引用指令序列中的 volatile 对象将在此之前发生volatile 写入编译后的二进制文件.
A write to a volatile object (volatile write) has Release semantics; a reference to a global or static object that occurs before a write to a volatile object in the instruction sequence will occur before that volatile write in the compiled binary.
一个 volatile 对象的读取(volatile read)具有 Acquire 语义;一个对读取后发生的全局或静态对象的引用指令序列中的易失性存储器将在之后发生volatile 读取编译后的二进制文件.
A read of a volatile object (volatile read) has Acquire semantics; a reference to a global or static object that occurs after a read of volatile memory in the instruction sequence will occur after that volatile read in the compiled binary.
但是,您可能会注意到,如果您点击上述链接,评论中会出现一些关于获取/释放语义实际上是否适用于这种情况的争论.
However, you might take note of the fact that if you follow the above link, there is some debate in the comments as to whether or not acquire/release semantics actually apply in this case.
相关文章