所有全局变量都应该是波动合格的吗?

在这个例子中,正确性是否要求将global_value声明为volatile

 int global_value = 0; void foo () { ++ global_value; } void bar () { some_function (++global_value); foo (); some_function (++global_value); } 

我的理解是volatile是“用于” 映射内存和变量的指针,可以通过信号修改 (并且强调不是为了线程安全)但是很容易想象bar可能会编译成这样的东西:

 push EAX mov EAX, global_value inc EAX push EAX call some_function call foo inc EAX push EAX call some_function mov global_value, EAX pop EAX 

这显然不正确,但即使没有volatile我认为根据C抽象机器它是有效的。 我错了还是有效?

如果是这样,在我看来, volatile常常被忽视。 这不是什么新鲜事 !


扩展示例

 void baz (int* i) { some_function (++*i); foo (); some_function (++*i); } int main () { baz (&global_value); } 

即使bar保证编译成正确的dont-cache-global_value实现, baz同样是正确的,还是允许缓存*i的非易失性值?

不,这里不需要volatile关键字。 由于global_value在函数bar外可见,因此如果调用另一个函数,编译器不能假定它保持不变。

[更新2011-07-28]我发现了一个很好的引用,certificate了这一切。 它是在ISO C99,5.1.2.3p2中,我太懒了,不能完全复制到这里。 它说:

在称为序列点的执行序列中的某些特定点处,先前评估的所有副作用应是完整的,并且不会发生后续评估的副作用。

序列点包括:

  • 在评估参数之后调用函数(6.5.2.2)。
  • 完整表达式的结束:[…]表达式语句中的表达式(6.8.3); […]

你有certificate。

volatile的唯一用途是涉及longjmp ,信号处理程序,内存映射设备驱动程序以及编写自己的低级multithreading同步原语。 然而,对于最后一次使用, volatile是不充分的,甚至可能不是必需的。 你肯定还需要asm(或编译器特定的或C1xprimefaces)来进行同步。

volatile不会用于任何其他目的,包括您询问的代码。

正如Roland所说,我不确定要引用的标准的哪一部分,“如果一个程序修改某些东西,那意味着该对象在抽象机器中被修改。如果一个程序使用一个值,那就意味着它使用任何值对象在抽象机器中有“。

volatile控制读取和写入内存的数量和顺序,但即使没有volatile ,将值作为优化缓存的实现也必须尊重抽象机器的行为。 这就是“假设”规则所说的,所以不服从的优化对我来说不是“容易想象”;-)你提出的代码对我来说显然是错误的,“写作可能会发生内存没有更新或弄脏L1缓存,因此未来的读取仍将看到缓存中的旧值“。 不是在单个核心上,它不会,因为行为相似的缓存会被破坏。

如果调用strcpy ,然后检查目标缓冲区的内容,则不允许编译器通过使用存储在寄存器中的该字节的先前值来“优化”。 strcpy不会使用volatile char * 。 同样, global_value不需要是volatile

我想混淆可能是在multithreading代码中,“然后”,也就是说,读取是在“写入”之后发生并因此“看到”新值,是由同步原语定义的。 在一些实现中,由于特定于实现的保证, volatile与同步有关。

在单线程代码中,在C和C ++标准中,“然后”由序列点定义,在给定的代码中有很多。

不应。全局变量不应总是声明为volatile。

如果它可以被其他线程更改并且可能遭受内存重新排序问题或编译器指令重新排序,那么您只需要它是易失性的。 即使这样,如果你有适当的静音,你也不需要它。 通常情况下,如果需要互斥全局变量,则可能设计不良。

编辑:使它变得不稳定并不意味着全局变量虽然是线程安全的!

其他典型用途可能是以不寻常的方式访问内存 – 例如,如果在嵌入式微型计算机上有一些DMA映射内存。

在这个例子中不需要易失性。 例如,如果some_function()输出某些内容,则asm列表似乎会更改c ++计算机的可观察行为并违反标准。

我猜这是编译器错误这是GCC汇编程序输出:

 .cfi_def_cfa_register 5 subl $24, %esp .loc 1 67 0 movl global_value, %eax addl $1, %eax movl %eax, global_value movl global_value, %eax movl %eax, (%esp) call _Z13some_functioni .loc 1 68 0 call _Z3foov .loc 1 69 0 movl global_value, %eax addl $1, %eax movl %eax, global_value movl global_value, %eax movl %eax, (%esp) call _Z13some_functioni .loc 1 70 0 leave .cfi_restore 5 

正如预期的那样,在函数调用之间重新加载global_value

挥发性也是线程安全的,简单来说v-qualifier在所有情况下都不足以满足线程安全(你有时需要额外关注primefaces性和内存障碍,但是有线通信变量应该是不稳定的…

[已编辑]: …如果它们被重复读取,可能会被读取之间的另一个线程更改。 但是,如果使用任何同步锁(互斥锁等),则不是这种情况,因为锁保证变量不能被任何并发活动更改)(感谢R ..)