C/C++和 C#/Java 之间使用易失性有什么区别?

2022-09-03 04:36:26

我在许多参考文献中发现它,其中提到在C / C++是弱的,并且可能导致在多个处理器上的并发环境中出现问题,但它()可以用作C#/ Java中不同CPU之间的通信机制。似乎这个关键字在C#/Java中比在C/C++中更严格,但是它们之间的区别/影响是什么?volatilevolatile

以下是 C/C++ 中的参考。为什么易失性在多线程C或C++编程中被认为没有用处?volatile


答案 1

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

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


答案 2

volatile 关键字对语言及其实现的平台具有高度主观性。虽然Java在所有架构中都提供了一致的易失性行为,但对于直接编译到本机机器平台的语言(例如C / C++)而言,情况并非如此。让我们试着理解为什么会这样。

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

v_n(a) \右箭头 v_{n+1}(a)

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

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

这是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关键字只保证计数变量不能相互重新排序,即。如果计数 == 2,则计数 = 1 必须位于它之前。但是,既不能保证 a == 1,也不能保证 b == 2。

在Java中,给定上面定义的更强的保证,那么如果计数== 1,则断言a == 1必须为真。同样,如果计数 == 2,则断言 a == 1 && b == 2 必须为真。这就是Java提供的严格内存保证的含义,C / C++没有。

然而,这并不意味着C/C++的行为与Java不同。是否这样做取决于(1)编译器是否执行任何代码重新排序,这些代码重新排序可能以令人惊讶的顺序,但合法顺序,以及(2)如果底层机器体系结构维护相同的严格内存顺序,前提是编译器不执行任何令人惊讶的代码重新排序。

例如,在所有x86平台上使用-O0在gcc上编译代码将符合(并且比Java的内存模型更严格),但其他架构(如PowerPC,Alpha,Itanium)都支持较弱的内存模型,该模型可能会表现出程序员可能意想不到的令人惊讶的程序行为。警告演讲者!

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


推荐