volatile,Interlocked:试图破坏代码

本文关键字:代码 Interlocked volatile | 更新日期: 2023-09-27 17:57:49

首先,我试图了解使用Interlocked是否仍然需要volatile字段定义,这是我真正的问题。

但是。由于懒得分析生成的MSIL,我决定在实践中进行检查。

我正在尝试MSDN示例中volatile的使用,当代码应该在带有优化的版本构建中中断时。什么也没坏。通过打开和关闭优化,代码运行良好(在这种情况下,主线程正常终止)

  1. 当我使用Interlocked从一个线程向字段写入并在没有锁的情况下从另一个线程读取时,我是否仍然需要字段上的volatile关键字
  2. 来自第1个问题的代码的简单示例volatile在哪里起作用
  3. 当我删除volatile关键字并在发行版中构建时,为什么MSDN示例仍然有效

用于说明问题1的代码片段。

class Example
{
    volatile int val;
    void Do()
    {
        Task.Run(() => { while (val == 0) Console.WriteLine("running"); });
        Thread.Sleep(1000);
        Interlocked.Increment(ref val);
        Console.WriteLine("done.");
        Console.ReadLine();
    }
}

volatile,Interlocked:试图破坏代码

如果变量没有标记为volatile,那么读取它的代码就无法知道它可能会被其他线程修改。所以JIT编译器不会知道将值保存在寄存器中可能是不安全的。

volatile是告诉JIT编译器其他线程可以修改该值的方式。其他线程在更改值时可能使用Interlocked是无关紧要的,因为JITer甚至可能不知道其他代码的存在!

您的简单示例之所以有效,可能是因为JIT编译器可以看到所有代码。如果这两个代码位在不同的方法中,甚至完全在不同的程序集中,情况可能会大不相同。

好吧,我成功地生成了volatile确实有影响,而Interlocked没有帮助的代码。您应该在没有调试器的情况下运行版本构建来测试它。

public class Example
{
    private volatile int val = 0;
    public static void Main()
    {
        var example = new Example();
        Task.Run(() => Interlocked.Increment(ref example.val));
        while (example.val == 0) ;
        // this never happens if val is not volatile
        Console.WriteLine("done.");
        Console.ReadLine();
    }
}

如果没有更详细的解释,我会接受@JimMischel的回答,因为它有一些解释。

更新:还发现这篇博客文章解释了一些细节。