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

时间:2009-08-13 07:36:25

标签: c# constructor singleton multithreading

  

可能重复:
  Is the C# static constructor thread safe?

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

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

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

谢谢!

2 个答案:

答案 0 :(得分:9)

  

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

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

  

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

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

然而:

  • 如果您的静态初始化程序本身在外部传递引用(通过调用带引用作为参数的方法(包括“arg0”),则所有投注均已关闭
  • 如果你使用反射来调用静态构造函数(是的,你可以这样做),疯狂常常跟随

答案 1 :(得分:2)

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

如果它可以执行多次,那只会是不安全的;如上所述,它不能,所以一切都很好:))