C#静态构造函数线程安全吗?

换句话说,这个Singleton实现是否是线程安全的:

public class Singleton { private static Singleton instance; private Singleton() { } static Singleton() { instance = new Singleton(); } public static Singleton Instance { get { return instance; } } } 

在创build任何类的实例或访问任何静态成员之前,静态构造函数保证只在每个应用程序域中运行一次。 http://msdn.microsoft.com/en-us/library/aa645612.aspx

显示的实现对于初始构造来说是线程安全的,也就是说,构buildSingleton对象不需要locking或空testing。 但是,这并不意味着任何对实例的使用都将被同步。 有很多方法可以做到这一点; 我已经在下面显示了一个。

 public class Singleton { private static Singleton instance; // Added a static mutex for synchronising use of instance. private static System.Threading.Mutex mutex; private Singleton() { } static Singleton() { instance = new Singleton(); mutex = new System.Threading.Mutex(); } public static Singleton Acquire() { mutex.WaitOne(); return instance; } // Each call to Acquire() requires a call to Release() public static void Release() { mutex.ReleaseMutex(); } } 

虽然所有这些答案都给出了相同的一般答案,但有一个警告。

请记住,generics类的所有潜在派生都被编译为单独的types。 因此,在为genericstypes实现静态构造函数时要小心。

 class MyObject<T> { static MyObject() { //this code will get executed for each T. } } 

编辑:

这是示范:

 static void Main(string[] args) { var obj = new Foo<object>(); var obj2 = new Foo<string>(); } public class Foo<T> { static Foo() { System.Diagnostics.Debug.WriteLine(String.Format("Hit {0}", typeof(T).ToString())); } } 

在控制台中:

 Hit System.Object Hit System.String 

实际上使用静态构造函数线程安全的。 静态构造函数保证只执行一次。

从C#语言规范http://msdn.microsoft.com/en-us/library/aa645612(VS.71).aspx :

一个类的静态构造函数在给定的应用程序域中最多执行一次。 静态构造函数的执行是由在应用程序域内发生以下第一个事件触发的:

  • 该类的一个实例被创build。
  • 任何类的静态成员都被引用。

所以是的,你可以相信,你的单身将被正确实例化。

Zooba提出了一个很好的观点(也是在我之前的15秒),静态构造函数将不保证对单例的线程安全的共享访问。 这将需要以另一种方式处理。

这是C#单例上面MSDN页面的Cliffnotes版本:

总是使用下面的模式,你不会出错:

 public sealed class Singleton { private static readonly Singleton instance = new Singleton(); private Singleton(){} public static Singleton Instance { get { return instance; } } } 

除了明显的单例function之外,它还免费提供了这两件事情(关于c ++中的单例):

  1. 懒惰的build设(如果没有被称为没有build设)
  2. 同步

静态构造函数保证每个App Domain只能触发一次,所以你的方法应该没问题。 但是,它在function上与更简洁的内联版本没有区别:

 private static readonly Singleton instance = new Singleton(); 

当你懒洋洋地初始化事物时,线程安全是更多的问题。

通用语言基础结构规范保证“对于任何给定types,types初始值设定项只能运行一次,除非用户代码明确调用。 (第9.5.3.1节)所以,除非在松散的调用Singleton ::。cctor上直接使用(不太可能)你的静态构造函数会在使用Singletontypes之前运行一次,否则只会创build一个Singleton实例,而你的实例属性是线程安全的。

请注意,如果Singleton的构造函数访问实例属性(即使是间接的),则实例属性将为空。 你可以做的最好的检测是什么时候发生这种情况,并通过检查属性访问器中的非空实例来引发exception。 在你的静态构造函数完成之后,Instance属性将是非空的。

正如Zoomba的答案所指出的那样,您将需要使Singleton安全地从多个线程访问,或者围绕使用单例实例实现locking机制。

只是迂腐,但没有静态构造函数,而是静态types初始值设定项, 这是循环静态构造函数依赖项的小演示,说明了这一点。

静态构造函数保证是线程安全的。 另外,请查看DeveloperZen对Singleton的讨论: http : //www.developerzen.com/2007/07/15/whats-wrong-with-this-code-1-discussion/

静态构造函数将任何线程被允许访问类之前 完成运行。

  private class InitializerTest { static private int _x; static public string Status() { return "_x = " + _x; } static InitializerTest() { System.Diagnostics.Debug.WriteLine("InitializerTest() starting."); _x = 1; Thread.Sleep(3000); _x = 2; System.Diagnostics.Debug.WriteLine("InitializerTest() finished."); } } private void ClassInitializerInThread() { System.Diagnostics.Debug.WriteLine(Thread.CurrentThread.GetHashCode() + ": ClassInitializerInThread() starting."); string status = InitializerTest.Status(); System.Diagnostics.Debug.WriteLine(Thread.CurrentThread.GetHashCode() + ": ClassInitializerInThread() status = " + status); } private void classInitializerButton_Click(object sender, EventArgs e) { new Thread(ClassInitializerInThread).Start(); new Thread(ClassInitializerInThread).Start(); new Thread(ClassInitializerInThread).Start(); } 

上面的代码产生了下面的结果。

 10: ClassInitializerInThread() starting. 11: ClassInitializerInThread() starting. 12: ClassInitializerInThread() starting. InitializerTest() starting. InitializerTest() finished. 11: ClassInitializerInThread() status = _x = 2 The thread 0x2650 has exited with code 0 (0x0). 10: ClassInitializerInThread() status = _x = 2 The thread 0x1f50 has exited with code 0 (0x0). 12: ClassInitializerInThread() status = _x = 2 The thread 0x73c has exited with code 0 (0x0). 

即使静态构造函数花费很长时间运行,其他线程停止并等待。 所有线程读取静态构造函数底部的_x值。

尽pipe其他答案大部分是正确的,但静态构造函数还有另外一个警告。

根据第II.10.5.3.3节ECMA-335通用语言基础结构的 种族和死锁

types初始化本身不应该创build一个死锁,除非一些从types初始值设定项(直接或间接)调用的代码明确地调用阻塞操作。

以下代码导致死锁

 using System.Threading; class MyClass { static void Main() { /* Won't run... the static constructor deadlocks */ } static MyClass() { Thread thread = new Thread(arg => { }); thread.Start(); thread.Join(); } } 

原作者是Igor Ostrovsky, 在这里看到他的post。