此C#类变量是否需要可变?

时间:2018-06-22 01:09:09

标签: c# multithreading volatile

在下面的C#代码中,仅从RunHelper()调用Run()。既然编译器知道它,它将以某种方式优化它,以使if(terminate)if(false)取代吗?

class Dummy
{
    private bool terminate;

    public Dummy() 
    {
        terminate = false;
    }

    public void KillDummy() // Called from another thread
    {
        terminate = true;
    }

    public void Run()
    {
        terminate = false;
        RunHelper();
    }
    private void RunHelper() //Only called from Run function
    {
        if(terminate)
        {
            Console.WriteLine("Exiting");
        }
    }
}

1 个答案:

答案 0 :(得分:2)

从C#编译到IL时,不会执行从RunHelper删除检查的优化。

但是,JIT(从IL编译为机器代码)可以在RunHelper中使用Runinline的副本来使调用无效,然后您担心可以进行优化赫本。 不,反射不会阻止这种情况,因为您只会看到带反射的IL,而看不到机器代码。


但是,即使没有进行优化,您也需要担心线程可见性。如果系统相对频繁地使用此代码,它将terminate保留在缓存中,并且-根据CPU体系结构※-其他线程可能不知道对其进行的更改。这就是为什么您应该使用volatile(或类似的解决方案)的原因。

※:在具有单个硬件线程(单核,没有超线程或类似技术)的计算机上没有问题。如果有多个硬件线程,它们都具有缓存,并且可能有也可能没有共享缓存。在这种情况下,您可能会冒着几乎永远看不见值的更改的风险。


据我所知,还有一些其他易失性警告,它们不会影响您的代码。

例如,重新排序不仅发生在编译器中。 Reorders can also happen in the CPU,也就是说,CPU可能不会严格按照接收机器代码的顺序执行机器代码(请参阅Out-of-order execution)。 volatile的使用在该重新排序上施加了限制。但是,读取后仍然可以移动写入(请参见:Threading in C# - PART 4: ADVANCED THREADING)。您将需要完整的内存屏障来修复它(.NET Core中为Interlocked.MemoryBarrier(),否则为Thread.MemoryBarrier())。

当然还有ABA problem。如果您面对它,则需要Interlocked来解决它,这也意味着您不能使用volatile,而只能使用Interlocked或易失性操作(Volatile.Read / { .NET Core中的{1}}或其他Volatile.Write / Thread.VolatileRead中的变量)来使用该变量,因为{{1} }不适用于Thread.VolatileWrite


另请参阅: