在下面的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");
}
}
}
答案 0 :(得分:2)
从C#编译到IL时,不会执行从RunHelper
删除检查的优化。
但是,JIT(从IL编译为机器代码)可以在RunHelper
中使用Run
和inline的副本来使调用无效,然后您担心可以进行优化赫本。 不,反射不会阻止这种情况,因为您只会看到带反射的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
。
另请参阅: