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

public class Singleton
{
    private static Singleton instance;

    private Singleton() { }

    static Singleton()
    {
        instance = new Singleton();
    }

    public static Singleton Instance
    {
        get { return instance; }
    }
}

当前回答

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

    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集合的值。

其他回答

虽然其他答案大多是正确的,但对于静态构造函数还有另一个警告。

根据ECMA-335通用语言的第二节10.5.3.3竞赛和死锁 基础设施

类型初始化本身不会产生死锁,除非有一些代码 从类型初始化器(直接或间接)显式调用 调用阻塞操作。

下面的代码导致死锁

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,见他的帖子在这里。

公共语言基础结构规范保证“除非用户代码显式地调用,否则对于任何给定类型,类型初始化器都只能运行一次。”(部分9.5.3.1。)所以除非你有一些古怪的IL在调用Singleton::。在使用Singleton类型之前,你的静态构造函数只会运行一次,只会创建一个Singleton实例,并且你的instance属性是线程安全的。

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

Zoomba的答案指出,你将需要使单例安全访问从多个线程,或实现一个锁定机制周围使用单例实例。

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

    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集合的值。

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

所示的实现对于初始构造是线程安全的,也就是说,构造Singleton对象不需要锁定或空测试。然而,这并不意味着对实例的任何使用都将同步。有很多种方法可以做到这一点;我在下面展示了一个。

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

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

来自c#语言规范:

类的静态构造函数在给定的应用程序域中最多执行一次。静态构造函数的执行由应用程序域中发生的以下事件中的第一个触发: 创建类的实例。 类的任何静态成员都被引用。

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

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