简体   繁体   English

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

[英]Does this c# class variable need to be volatile?

In the below C# code, RunHelper() is only called from Run() . 在下面的C#代码中, RunHelper() Run()调用RunHelper() Run() Since the compiler knows about it, will it optimize it in such a way that if(terminate) is going to be replaced by if(false) ? 既然编译器知道它,它将以将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");
        }
    }
}

When compiling from C# to IL, the optimization removing the check from RunHelper will not happen. 从C#编译到IL时,从RunHelper删除检查的优化不会发生。

However, the JIT (that compiles from IL to machine code) could take RunHelper and inline a copy inside of Run to void the call, and then the optimization you fear can heppen. 但是,JIT(从IL编译为机器代码)可以使用RunHelper并在Run 内联一个副本以使调用无效,然后您担心会导致优化失败。 No, reflection does not prevent this, because you will only see the IL with reflection, but not the machine code. 不,反射不会阻止这种情况,因为您只会看到带反射的IL,而看不到机器代码。


However, even if that optimization does not happen, you need to worry about thread visibility. 但是,即使没有进行优化,您也需要担心线程可见性。 If the system uses this code relatively often, it will keep terminate in cache, and - depending on the CPU architecture※ - other threads may not be aware of changes made to it. 如果系统相对频繁地使用此代码,它将在缓存中terminate ,并且-根据CPU体系结构※-其他线程可能不知道对其所做的更改。 This is why you should use volatile (or similar solution). 这就是为什么您应该使用volatile (或类似解决方案)的原因。

※: On a computer with a single hardware thread (single core, and no hyper-threading or similar technology) there is no problem. ※:在具有单个硬件线程(单核,没有超线程或类似技术)的计算机上,没有问题。 If there are multiple hardware threads, they all have a cache, and there may or may not be shared caches. 如果有多个硬件线程,它们都具有缓存,并且可能有也可能没有共享缓存。 In those cases, you risk the changes to the value being invisible virtually forever. 在这些情况下,您可能会冒着几乎永远看不见值更改的风险。


There are some other caveats with volatile that - as far as I can tell - do not affect your code. 据我所知,还有其他一些关于volatile的警告不影响您的代码。

For instance, reordering does not only happen in the compiler. 例如,重新排序不仅发生在编译器中。 Reorders can also happen in the CPU , that is, the CPU may not execute the machine code strictly in the order it recieve them (see Out-of-order execution ). 重新排序也可能在CPU中发生 ,也就是说,CPU可能不会严格按照接收代码的顺序执行机器代码(请参阅无序执行 )。 The use volatile puts contraints on that reordering. 使用volatile在该重新排序上施加了约束。 However, writes could still be moved after reads (see: Threading in C# - PART 4: ADVANCED THREADING ). 但是,读取后仍然可以移动写入(请参阅: C#中的线程-第4部分:高级线程 )。 You will be needing full memory barrier to fix it ( Interlocked.MemoryBarrier() in .NET Core and Thread.MemoryBarrier() otherwise). 您将需要完整的内存屏障来修复它(.NET Core中的Interlocked.MemoryBarrier()以及否则的Thread.MemoryBarrier() )。

And, of course, there is the ABA problem . 当然,还有ABA问题 If you face it, you would need Interlocked to solve it, which also means you cannot use volatile and will have to only use Interlocked or volatile operations ( Volatile.Read / Volatile.Write in .NET Core or Thread.VolatileRead / Thread.VolatileWrite otherwise) to work with the variable, forcing you to change from bool to int , because Interlocked does not work with bool . 如果遇到问题,则需要使用Interlocked来解决它,这也意味着您不能使用volatile ,而只能使用Interlocked或volatile操作(.NET Core中的Volatile.Read / Volatile.WriteThread.VolatileRead / Thread.VolatileWrite否则)使用变量,迫使您从bool更改为int ,因为Interlocked不适用于bool


See also: 也可以看看:

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM