Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/335.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 说明volatile关键字在C中的用法#_C#_.net_Volatile - Fatal编程技术网

C# 说明volatile关键字在C中的用法#

C# 说明volatile关键字在C中的用法#,c#,.net,volatile,C#,.net,Volatile,我想编写一个小程序,直观地演示volatile关键字的行为。理想情况下,它应该是一个对非易失性静态字段执行并发访问的程序,并因此而获得不正确的行为 在同一程序中添加volatile关键字可以解决此问题 这是我没能做到的。即使尝试了几次,启用了优化等等,我总能在没有'volatile'关键字的情况下获得正确的行为 你对这个话题有什么想法吗?你知道如何在一个简单的演示应用程序中模拟这样的问题吗?它依赖于硬件吗?是的,它依赖于硬件(如果没有多个处理器,您不太可能看到问题),但它也依赖于实现。CLR规范

我想编写一个小程序,直观地演示
volatile
关键字的行为。理想情况下,它应该是一个对非易失性静态字段执行并发访问的程序,并因此而获得不正确的行为

在同一程序中添加volatile关键字可以解决此问题

这是我没能做到的。即使尝试了几次,启用了优化等等,我总能在没有'volatile'关键字的情况下获得正确的行为


你对这个话题有什么想法吗?你知道如何在一个简单的演示应用程序中模拟这样的问题吗?它依赖于硬件吗?

是的,它依赖于硬件(如果没有多个处理器,您不太可能看到问题),但它也依赖于实现。CLR规范中的内存模型规范允许一些CLR的Microsoft实现不一定要做的事情。

如果没有指定'volatile'关键字,则实际上不是发生故障的问题,如果没有指定,则更可能发生错误。通常情况下,您会比编译器更了解这种情况

考虑这一点最简单的方法是,如果编译器愿意,它可以内联某些值。通过将该值标记为volatile,您告诉自己和编译器该值实际上可能会更改(即使编译器不这么认为)。这意味着编译器不应在线生成值、保留缓存或提前读取值(以尝试优化)

<>这个行为并不是C++中的关键字。

MSDN有一个简短的描述。
这里可能有一篇关于

主题的更深入的文章,很难用C#来演示,因为代码是由虚拟机抽象的,因此在这台机器的一个实现上,它可以正常工作而不易失,而在另一个实现上可能会失败

如果JIT编译器确定变量的值无论如何都不能更改,从而创建了不再检查它的机器代码,那么在C#中也可能发生同样的情况。如果现在另一个线程正在更改该值,那么您的第一个线程可能仍会在循环中被捕获


同样,这种情况也可能发生在C#上,但这在很大程度上取决于虚拟机和JIT编译器(或解释器,如果它没有JIT…理论上,我认为MS总是使用JIT编译器,Mono也使用JIT编译器;但您可以手动禁用它)。

我已经实现了一个工作示例

主要思想来自wiki,但对C#做了一些修改。Wiki文章演示了C++的静态字段,看起来C总是总是仔细地把请求编译成静态字段…我举了一个非静态的例子:

如果您在发布模式下运行此示例,并且不使用调试器(即使用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");
    }
}

以下是我对集体理解这种行为的贡献。。。这并不多,只是一个演示(基于xkip的演示),它显示了在同一个程序中,一个volatile与一个非volatile(即“正常”)int值并排的行为。。。这就是我找到这根线时一直在找的东西

using System;
using System.Threading;

namespace VolatileTest
{
  class VolatileTest 
  {
    private volatile int _volatileInt;
    public void Run() {
      new Thread(delegate() { Thread.Sleep(500); _volatileInt = 1; }).Start();
      while ( _volatileInt != 1 ) 
        ; // Do nothing
      Console.WriteLine("_volatileInt="+_volatileInt);
    }
  }

  class NormalTest 
  {
    private int _normalInt;
    public void Run() {
      new Thread(delegate() { Thread.Sleep(500); _normalInt = 1; }).Start();
      // NOTE: Program hangs here in Release mode only (not Debug mode).
      // See: http://stackoverflow.com/questions/133270/illustrating-usage-of-the-volatile-keyword-in-c-sharp
      // for an explanation of why. The short answer is because the
      // compiler optimisation caches _normalInt on a register, so
      // it never re-reads the value of the _normalInt variable, so
      // it never sees the modified value. Ergo: while ( true )!!!!
      while ( _normalInt != 1 ) 
        ; // Do nothing
      Console.WriteLine("_normalInt="+_normalInt);
    }
  }

  class Program
  {
    static void Main() {
#if DEBUG
      Console.WriteLine("You must run this program in Release mode to reproduce the problem!");
#endif
      new VolatileTest().Run();
      Console.WriteLine("This program will now hang!");
      new NormalTest().Run();
    }

  }
}
上面有一些非常精练的解释,还有一些很好的参考资料。感谢大家帮助我了解了
volatile
(至少知道我的第一本能是
lock
它,而不是依赖
volatile

干杯,谢谢所有的鱼。基思


PS:我对原始请求的演示非常感兴趣,它是:“我希望看到静态volatile int在静态int行为不正常的情况下正确运行

我尝试过这个挑战,但失败了。(实际上我很快就放弃了;-)。在我尝试过的所有静态变量中,它们的行为都“正确”“不管它们是否易变。。。我想解释一下为什么会是这样,如果真的是这样的话。。。编译器是否不在寄存器中缓存静态变量的值(即,它缓存对该堆地址的引用)


不,这不是一个新问题。。。这是试图让社区回到原来的问题。

我看到了乔·阿尔巴哈里(Joe Albahari)的以下文字,这对我帮助很大

我从上面的文本中抓取了一个例子,通过创建一个静态volatile字段,我对它做了一些修改。当您删除
volatile
关键字时,程序将无限期地阻塞。在发布模式下运行此示例

class Program
{
    public static volatile bool complete = false;

    private static void Main()
    {           
        var t = new Thread(() =>
        {
            bool toggle = false;
            while (!complete) toggle = !toggle;
        });

        t.Start();
        Thread.Sleep(1000); //let the other thread spin up
        complete = true;
        t.Join(); // Blocks indefinitely when you remove volatile
    }
}

在.NET4.0上测试,x86和x64都可以确认该示例仍然适用。谢谢!:)太好了!我从来都不知道JIT会进行这种优化,而volatile会禁用它。为了明确起见,您正在将“volatile”关键字添加到foo的声明中,对吗?这个示例很有效。我确信我遇到的问题(线程卡在循环中)与波动性有关(或缺乏波动性),但即使在人工创建场景后也无法重现,直到我打开优化。将“volatile”放在bool上修复了无限循环的问题,但是现在代码中的其他地方也有这样做的问题,所以决定关闭优化。这让我有点惊讶,或者说打开优化(这是最近才做的)会让人大吃一惊。