LazyInitializer.EnsureInitialized中的易失性局部变量?

时间:2012-01-02 15:04:38

标签: c# volatile

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

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

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

  3. 有谁知道这是哪种情况(或者是否有其他解释)?如果是反编译问题,是否有人知道“真正的”代码会是什么样的?

2 个答案:

答案 0 :(得分:3)

这看起来像一个Reflector错误:它只是s_barrier字段的普通易失性读取。这里没有“特殊”的IL,这在C#中是不可表达的。

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

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


这是一个更简单的repro:只需在 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#编译器优化了字段值(易失性读取的结果)到局部变量(stloc指令)的赋值,因为本地不是随后使用。这似乎混淆了Reflector。如果您更改了方法以使用随后使用的local,那么确实会发出stloc(或类似的)指令,然后反射器的反编译输出看起来很合理。

答案 1 :(得分:2)

Ani是对的。这是从Reference Source检索到的实际源代码。反射器很不错,但不能对实际注释的源代码持蜡烛。

    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编写代码的快乐提出了一些见解。