使用集合初始值设定项时,另一个线程可以看到部分创建的集合吗?

2024-01-05

想象一下某种方法中的 C# 代码:

SomeClass.SomeGlobalStaticDictionary = new Dictionary<int, string>()
{
    {0, "value"},
};

假设没有人使用任何显式内存屏障或锁定来访问字典。

如果没有进行优化,则全局字典应该为 null(初始值)或具有一个条目的正确构造的字典。

问题是:是否可以重新排序 Add 调用和分配给 SomeGlobalStaticDictionary 的效果,以便其他线程会看到一个空的非空 SomeGlobalStaticDictionary (或任何其他无效的部分构造的字典?)

如果 SomeGlobalStaticDictionary 是不稳定的,答案会改变吗?

看完之后http://msdn.microsoft.com/en-us/magazine/jj863136.aspx http://msdn.microsoft.com/en-us/magazine/jj863136.aspx(也是它的第二部分)我了解到,理论上,仅仅因为在源代码中分配了一个变量,其他线程可能会由于多种原因而以不同的方式看到它。我查看了 IL 代码,但问题是是否允许 JIT 编译器和/或 CPU 在分配 SomGlobalStaticDictionary 之前不“刷新”对其他线程的 Add 调用的效果。


在局部变量中,打开优化后,编译器will(至少有时)编译为首先分配给变量,然后调用的代码Add(或为对象初始值设定项设置属性)。

如果您使用静态变量或实例变量,您会看到不同的行为:

class Test
{
    static List<int> StaticList = new List<int> { 1 };
    List<int> InstanceList = new List<int> { 2 };
}

给出以下类型初始值设定项 IL:

.method private hidebysig specialname rtspecialname static 
        void  .cctor() cil managed
{
  // Code size       21 (0x15)
  .maxstack  2
  .locals init (class [mscorlib]System.Collections.Generic.List`1<int32> V_0)
  IL_0000:  newobj     instance void class [mscorlib]System.Collections.Generic.List`1<int32>::.ctor()
  IL_0005:  stloc.0
  IL_0006:  ldloc.0
  IL_0007:  ldc.i4.1
  IL_0008:  callvirt   instance void class [mscorlib]System.Collections.Generic.List`1<int32>::Add(!0)
  IL_000d:  nop
  IL_000e:  ldloc.0
  IL_000f:  stsfld     class [mscorlib]System.Collections.Generic.List`1<int32> Test::StaticList
  IL_0014:  ret
} // end of method Test::.cctor

以及以下构造函数 IL:

.method public hidebysig specialname rtspecialname 
        instance void  .ctor() cil managed
{
  // Code size       29 (0x1d)
  .maxstack  3
  .locals init (class [mscorlib]System.Collections.Generic.List`1<int32> V_0)
  IL_0000:  ldarg.0
  IL_0001:  newobj     instance void class [mscorlib]System.Collections.Generic.List`1<int32>::.ctor()
  IL_0006:  stloc.0
  IL_0007:  ldloc.0
  IL_0008:  ldc.i4.2
  IL_0009:  callvirt   instance void class [mscorlib]System.Collections.Generic.List`1<int32>::Add(!0)
  IL_000e:  nop
  IL_000f:  ldloc.0
  IL_0010:  stfld      class [mscorlib]System.Collections.Generic.List`1<int32> Test::InstanceList
  IL_0015:  ldarg.0
  IL_0016:  call       instance void [mscorlib]System.Object::.ctor()
  IL_001b:  nop
  IL_001c:  ret
} // end of method Test::.ctor

在这两种情况下,集合都会被填充before该字段已设置。现在这并不是说可能不再存在内存模型问题,但它是not与设置为引用空集合的字段相同,然后Add正在拨打电话。从分配线程的角度来看,分配发生在Add.

一般来说,对象初始值设定项和集合初始值设定项表达式都相当于使用临时变量构造对象 - 因此,在赋值中使用它的情况下,属性设置器都会被调用before分配发生。

然而我不相信any针对对象/集合初始值设定项对其他线程的可见性给出了特殊保证。我建议您想象一下如果按照规范“手写”地写出代码会是什么样子,然后从那里进行推理。

There are为静态初始化程序和构造函数提供的保证 - 但主要是在 .NET 的 Microsoft 实现中,而不是“一般”保证(例如在 C# 规范或 ECMA 规范中)。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用集合初始值设定项时,另一个线程可以看到部分创建的集合吗? 的相关文章

随机推荐