C#中的'volatile'关键字有什么作用?

18

volatile关键字在C#中的作用是什么?

我在哪些情况下需要使用这个关键字?

我看到了下面的语句,但不理解为什么需要使用volatile

internal volatile string UserName; 
6个回答

30
我参考规范的第10.5.3节,其中指出:
对于非易失性字段,重新排序指令的优化技术可能会导致多线程程序中访问未经同步的字段(例如lock语句(§8.12)提供的同步)而产生意外和不可预测的结果。这些优化可以由编译器、运行时系统或硬件执行。对于易失性字段,这种重新排序优化是受限制的:
读取易失性字段称为易失性读取。易失性读取具有"获取语义";也就是说,在指令序列中出现在它之后的任何内存引用之前,保证已经完成了。
写入易失性字段称为易失性写入。易失性写入具有"释放语义";也就是说,在指令序列中先于写入指令的任何内存引用之后,保证已经完成了。
这些限制确保所有线程都将按照它们执行的顺序观察到其他任何线程执行的易失性写入。符合要求的实现不需要提供从所有执行线程所看到的易失性写入的单个总排序。
如果您有意使用易失性字段,请非常仔细地阅读此内容。如果您没有完全和彻底地理解易失性语义的所有含义,则不要尝试使用它们。通常更好的选择是使用锁,锁会自动为您提供足够的内存屏障以确保必要的获取和释放语义。请记住,在争用时锁才会真正变得昂贵。

@Shimmy:你完全有能力在没有我的帮助下找到规格说明。 - Eric Lippert

7

Volatile用于表示变量可以在代码运行时自动更改。它告诉编译器以不缓存变量的方式编写汇编代码,而是在每次使用之前确保读取它。

一个示例是硬件寄存器,您的代码已经将其映射到内存并正在读取以确定何时设置标志。硬件可能在代码运行时设置值,如果没有使用volatile关键字,则不会注意到此更改,因为汇编代码实际上不会检查该值。


6

volatile关键字是给编译器(以及ngen/jit编译器)的一个提示,告诉它这个变量的值随时可能会改变,因此访问变量的优化,比如将变量的值缓存到本地,应该被禁用。

考虑下面的代码:

If (UserName == "")
    // do something
If (UserName == "Fred")
    // do something

如果没有volatile关键字,编译器可能会生成IL代码,在第一次比较时将引用存储在堆栈上,然后在第二次比较时重复使用它。但是,添加volatile告诉编译器该引用可能会被另一个线程更改,从而强制它生成不会重用第一次比较的堆栈副本的IL代码。

你的意思是每次访问变量都会生成新的IL,而不是使用堆栈副本,对吗? - Zain Shaikh
是的,如果存在volatile,则编译器将生成新的IL指令来获取每个字段访问的字段值。如果不存在volatile,则强制执行可能会跳过第二个和后续填充访问的IL。 - Franci Penov
值得注意的是,这仅适用于多线程应用程序。一个线程可能会读取用户名,然后CPU切换到另一个线程,它更新了用户名,然后CPU再次切换回来。除非重新检查,否则第一个线程现在具有旧版本的用户名。 - jb.

4

MSDN将会比我更好地进行总结...

"volatile"关键字表示某个字段可能被多个同时执行的线程修改。声明为“volatile”的字段不会受到编译器假设只有单个线程访问的优化影响。这确保了该字段中始终存在最新的值。

http://msdn.microsoft.com/en-us/library/x13ttww7(v=VS.100).aspx


3

2

'volatile'的作用是告诉编译器,这个变量可能随时会被任何方式改变其值,编译器不能对此变量进行任何假设。

通常编译器会假定某些变量在运行时是常量。这可能导致反复检查寄存器值时出现错误,因为寄存器值可能会被改变。因此,对于这些类型的变量,应该声明为 'volatile',并且每次出现在代码中都应该进行检查,而不带任何假设。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接