代码之家  ›  专栏  ›  技术社区  ›  urini

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

  •  228
  • urini  · 技术社区  · 16 年前

    换句话说,这个单例实现是线程安全的吗:

    public class Singleton
    {
        private static Singleton instance;
    
        private Singleton() { }
    
        static Singleton()
        {
            instance = new Singleton();
        }
    
        public static Singleton Instance
        {
            get { return instance; }
        }
    }
    
    10 回复  |  直到 16 年前
        1
  •  199
  •   Wollmich    5 年前

    在创建类的任何实例或访问任何静态成员之前,每个应用程序域只能运行一次静态构造函数。 https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/static-constructors

    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();
        }
    }
    
        2
  •  91
  •   Markus Safar    8 年前

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

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

    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
    
        3
  •  30
  •   Peter Duniho    5 年前

    线程安全。静态构造函数保证只执行一次。

    From the C# language specification

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

    • 将创建该类的一个实例。

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

        4
  •  9
  •   Jay Juch    13 年前

    以下是c#singleton上上述MSDN页面的Cliffnotes版本:

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

    除了明显的单例特性之外,它还免费提供以下两个功能(关于c++中的单例):

    1. 惰性构造(如果从未调用过,则不进行构造)
        5
  •  6
  •   Andrew Peters    16 年前

    静态构造函数保证每个应用程序域只触发一次,所以您的方法应该是正确的。但是,它在功能上与更简洁的内联版本没有什么不同:

    private static readonly Singleton instance = new Singleton();
    

    当您懒洋洋地初始化事物时,线程安全更是一个问题。

        6
  •  5
  •   Trade-Ideas Philip    8 年前

    跑步 之前 允许任何线程访问该类。

        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值。

        7
  •  3
  •   Community Reversed Engineer    7 年前

    这个 Common Language Infrastructure specification

    请注意,如果Singleton的构造函数访问实例属性(甚至是间接访问),那么实例属性将为null。您所能做的最好的事情就是通过在属性访问器中检查实例是否为非null来检测何时发生这种情况并引发异常。静态构造函数完成后,实例属性将为非null。

    Zoomba's answer 指出您需要使Singleton安全地从多个线程访问,或者在使用Singleton实例时实现锁定机制。

        8
  •  2
  •   Sam Chad    10 年前

    只是为了学究,但是没有静态构造函数,而是静态类型初始值设定项, here's a small 演示循环静态构造函数依赖关系,说明了这一点。

        9
  •  2
  •   Theodor Zoulias    4 年前

    尽管其他答案基本正确,但静态构造函数还有另一个警告。

    按章节 二、 10.5.3.3座圈和死锁 ECMA-335 Common Language Infrastructure

    从类型初始值设定项(直接或间接)显式调用

    以下代码导致死锁

    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();
        }
    }
    

    原作者是伊戈尔·奥斯特罗夫斯基,见他的帖子 here .