LazyInitializer.EnsureInitialized中的Volatile局部变量

本文关键字:局部变量 Volatile 中的 EnsureInitialized LazyInitializer | 更新日期: 2023-09-27 18:19:09

我在Reflector中查看LazyInitializer.EnsureInitialized(ref T, Func{T}),并且该方法volatile object local1 = s_barrier;中似乎有一个易失性的局部变量. 我可以想到两个可能的原因:

  1. 。. NET可能会使用给定语言不支持的特性,或者

  2. 实际代码没有声明一个易失性局部变量,但是当编译后的代码被Reflector反编译时,它看起来像一个易失性局部变量。

有谁知道这里是哪种情况(或者是否可能有其他解释)?如果这是一个反编译的问题,有人知道"真正的"代码应该是什么样子吗?

LazyInitializer.EnsureInitialized中的Volatile局部变量

这看起来像一个反射器的错误:它只是一个正常的易失性读取s_barrier字段。这里没有不能在c#中表达的"特殊"IL。

 L_000d: volatile. 
 L_000f: ldsfld object modreq(System.Runtime.CompilerServices.IsVolatile) System.Threading.LazyInitializer::s_barrier

这只是编译器从静态volatile字段读取时发出的正常代码。


这里有一个更简单的复制:只需以release模式编译以下代码(用类型包装):

private static volatile object field;
private static void Main()
{
    var temp = field;
}

Reflector生成以下反编译c#:

private static void Main()
{
    volatile object field = Program.field;
}

当IL实际上是:

L_0000: volatile. 
L_0002: ldsfld object modreq([mscorlib]System.Runtime.CompilerServices.IsVolatile) WindowsFormsApplication1.Program::field
L_0007: pop 
L_0008: ret 

:以下是我对发生的事情的猜测:在发布模式下,c#编译器优化了将字段的值(volatile read的结果)赋值给局部变量(stloc指令),因为随后不使用局部变量。这似乎混淆了Reflector。如果您将方法更改为使用随后的use局部,则确实会发出stloc(或类似的)指令,然后从Reflector反编译的输出看起来是合理的。

阿尼是对的。这是从参考源中检索到的实际源代码。反射器很好,但不能与实际注释的源代码相提并论。

    public static T EnsureInitialized<T>(ref T target) where T : class
    {
        // Fast path.
        if (target != null)
        {
            object barrierGarbage = s_barrier; // Insert a volatile load barrier. Needed on IA64.
            return target;
        }
        return EnsureInitializedCore<T>(ref target, LazyHelpers<T>.s_activatorFactorySelector);
    }

这个命名的选择也让我们了解了微软程序员是如何看待为Titanium编写代码的乐趣的。