静态初始化保证单线程安全吗?(C#)

7 c# singleton multithreading constructor

可能重复:
C#静态构造函数线程是否安全?

Jon Skeet在http://csharpindepth.com/Articles/General/Singleton.aspx上的精彩文章以及我读过的其他文章清楚地表明,双重检查锁定在C#和Java中都不起作用,除非有明确标记实例为"易变".如果不这样做,则将其与null进行比较的检查可能会返回false,即使实例构造函数尚未完成运行.在Skeet先生的第三个样本中,他清楚地说明了这一点:"Java内存模型不能确保构造函数在将新对象的引用分配给实例之前完成.Java内存模型经历了1.5版的重新修改,但是在没有volatile变量的情况下,-check锁定仍然被破坏(如在C#中)

但是,大多数人都同意(包括Skeet先生,他的文章中的第4和第5个样本),使用静态初始化是获取线程安全单例实例的简单方法.他声称"C#中的静态构造函数被指定仅在创建类的实例或引用静态成员时执行,并且每个AppDomain只执行一次."

这是有道理的,但似乎缺少的是保证仅在构造函数完成后才分配对新对象的引用 - 否则我们会遇到使得双重检查锁定失败的同类问题,除非您标记实例像挥发性的.是否有保证,当使用静态初始化来调用实例构造函数(而不是从属性的get {}调用实例构造函数时,就像我们使用双重检查锁定一样),构造函数将在任何其他线程之前完全完成可以获得对象的引用?

谢谢!

Mar*_*ell 9

在任何其他线程可以获得对象的引用之前,构造函数是否会完全完成?

静态初始化程序将仅(通过系统,至少)每次调用一次AppDomain,并以同步方式调用"beforefieldinit".因此,假设您没有做任何奇怪的事情,静态初始化程序中分配的任何静态字段应该没问题; 任何其他使用静态字段的尝试都应该在静态构造函数后面保持(阻塞).

只有在构造函数完成后才会分配对新对象的引用

它会在它发生时发生.例如,任何静态字段初始化器都会您通常认为是构造函数之前发生.但由于其他线程被阻止,这应该不是问题.

然而:

  • 如果你的静态初始值设定项本身传递了一个引用(通过调用一个带引用作为参数的方法(包括"arg0")),那么所有的赌注都会关闭
  • 如果你使用反射来调用静态构造函数(是的,你可以这样做),经常会出现疯狂


Noo*_*ilk 2

是的; 声明中保证每个 AppDomain 只会执行一次。

只有当它可以执行多次时才可能是不安全的;如前所述,它不能,所以一切都很好:)