C / C ++和C#/ Java之间使用volatile的区别是什么?

我在许多参考文献中发现它提到C / C ++中的volatile很弱并且可能在多处理器的并发环境中引起问题,但它( volatile )可以用作C#/ Java中差异CPU之间的通信机制。 看来这个关键字在C#/ Java中比在C / C ++中更严格,但它们之间的差异/影响是什么?

这是C / C ++中volatile的引用。 为什么volatile在multithreadingC或C ++编程中不被认为有用?

对于C#/ Java ,“ volatile ”告诉编译器必须永远不要缓存变量的值,因为它的值可能会在程序本身的范围之外发生变化。 然后,如果变量“在其控制范围之外”,则编译器将避免可能导致问题的任何优化。

C / C ++中 ,开发嵌入式系统或设备驱动程序时需要“ volatile ”,您需要读取或写入内存映射的硬件设备。 特定设备寄存器的内容可能随时更改,因此您需要“ volatile ”关键字以确保编译器不会优化此类访问。

volatile关键字对于语言及其实现的平台是高度主观的。 虽然Java在所有体系结构中提供了一致的volatile性能,但直接编译到本机机器平台的语言并不是这种情况,例如在C / C ++的情况下。 让我们试着理解为什么会这样。

设a,b是一组程序动作P的成员,而v_ {n}(a)是将波动率要求应用于动作的函数,其中下标_n表示应用易失性动作的第_n次迭代,和\ rightarrow是先前的运算符,如前所述。 对于所有程序操作,以下规则成立:

v_n(a)\ rightarrow v_ {n + 1}(a)

a \ rightarrow v_n(b)\ Rightarrow a \ rightarrow v_ {n + i}(b)其中i \ in \ mathbb {N}

规则1表示所有易失性函数都强制执行总顺序,其中函数v_ {n}(a)始终位于v_ {n + 1}(a)之前,其中规则2表示如果操作a先于动作的volatile函数b在第_n次迭代中,动作a必须先于应用于b的所有后续volatile函数。

这是Java中非常强大的内存需求,实际上它比C / C ++强得多。 C / C ++语言规范对内存排序没有这样的限制,并将其留给编译器实现来决定如何围绕易失性操作排序非易失性操作。

让我们考虑这些规则如何通过简单的代码示例影响程序执行:

 int a = 0; int b = 0; volatile int count = 0; a = 1; count = 1; b = 2; count = 2; 

在C / C ++中,volatile关键字只保证count变量不能相互重新排序,即。 如果count == 2,则count = 1必须先于它。 但是,既不保证a == 1,也不保证b == 2。

在Java中,给定上面定义的更强保证,那么如果count == 1,那么断言a == 1必须为真。 类似地,如果count == 2,则断言a == 1 && b == 2必须为真。 这就是Java提供的严格的内存保证意味着C / C ++没有。

但是,这并不意味着C / C ++的行为方式与Java不同。 是否这样做取决于(1)编译器是否执行任何可能令人惊讶的顺序但是合法顺序的代码重新排序,以及(2)基础机器架构是否支持相同的严格内存顺序,前提是编译器不执行任何令人惊讶的代码重新排序。

例如,在所有x86平台上使用-O0设置的gcc上编译代码将符合(并且比Java内存模型更严格),但其他体系结构(如PowerPC,Alpha,Itanium)都支持较弱的内存模型,这可能会出现令人惊讶的程序程序员可能不会期望的行为。 警告Lector!

无论如何,如果您对更多内存模型一致性规则感兴趣,您可能希望观看英特尔对x86内存模型的解释,其中详细解释了内存排序的细微差别。 请享用!

在C / C ++中, volatile没有与multithreading相关的特定语义,因此它在该上下文中的行为是特定于平台的。 C#和Java为volatile提供特定的multithreading语义。 所以你知道你得到了什么,可以依靠它。