说明C#中volatile关键字的用法
我想编写一个小程序,以可视方式说明" volatile"关键字的行为。理想情况下,它应该是对非易失性静态字段执行并发访问并因此而获得错误行为的程序。
在同一程序中添加volatile关键字应该可以解决此问题。
这是我无法实现的。即使尝试了几次,启用了优化等等,我也总是在没有'volatile'关键字的情况下获得正确的行为。
我们对此主题有任何想法吗?我们知道如何在简单的演示应用程序中模拟此类问题吗?它取决于硬件吗?
解决方案
是的,它取决于硬件(如果没有多个处理器,我们不太可能看到问题),但是它也取决于实现。 CLR规范中的内存模型规范允许Microsoft实施CLR不一定要做的事情。我在volatile关键字上看到的最好的文档是Joe Duffy的这篇博客文章。请注意,他说MSDN文档"具有极大的误导性"。
当未指定'volatile'关键字时,发生错误并不是真正的问题,更多的是,未指定关键字时可能会发生错误。通常情况下,我们会比编译器更了解这种情况!
最简单的思考方法是,如果愿意,编译器可以内联某些值。通过将值标记为易失性,我们可以告诉自己和编译器该值实际上可能会更改(即使编译器认为不是这样)。这意味着编译器不应内联值,保持高速缓存或者及早读取值(以进行优化)。
这种行为与C ++中的关键字并不完全相同。
MSDN在这里有简短描述。
这可能是有关波动率,原子性和联锁性的更深入的文章
很难用C#演示,因为代码是由虚拟机抽象的,因此,在该计算机的一种实现上,它可以正常工作而不会不稳定,而在另一种实现上可能会失败。
维基百科有一个很好的例子,说明了如何用C语言进行演示。
如果JIT编译器确定该变量的值无论如何都无法更改,则可能会发生同样的事情,从而创建了甚至不再检查它的机器代码。如果现在另一个线程正在更改该值,则第一个线程可能仍会陷入循环。
另一个例子是忙等待。
同样,Cas可能会发生这种情况,但是它在很大程度上取决于虚拟机和JIT编译器(或者解释器,如果它没有JIT ...从理论上讲,我认为MS始终使用JIT编译器,而Mono使用;但是我们可以手动将其禁用)。
我已经取得了一个可行的例子!
主要思想来自Wiki,但对C#进行了一些更改。 Wiki文章针对C ++的静态字段演示了这一点,似乎Calways仔细地将请求编译为静态字段...并且我以非静态示例为例:
如果我们在发布模式下且没有调试器的情况下(即使用Ctrl + F5)运行此示例,则while(test.foo!= 255)'行将被优化为'while(true)',并且该程序永不返回。 但是在添加
volatile`关键字后,我们总是会得到'OK'。
class Test { /*volatile*/ int foo; static void Main() { var test = new Test(); new Thread(delegate() { Thread.Sleep(500); test.foo = 255; }).Start(); while (test.foo != 255) ; Console.WriteLine("OK"); } }