C# 单例

105 阅读2分钟

一般常见写法,但是不是线程安全的,在多个线程访问 if (instance == null) 时会,同时进入if语句内创建多个Singleton实例。

public class Singleton
{
    // 直接初始化单例实例
    private static Singleton instance ;
    public static Singleton Instance
    {
        get
        {
            if (instance != null)
            {
                return instance;
            }

            if (instance == null)
            {
                instance = new Singleton();
            }

            return instance;
        }
    }

    // 私有构造函数
    private Singleton()
    {
        // 这里可以进行一些初始化工作
        Console.WriteLine("Singleton instance created.");
    }

    public void SomeMethod()
    {
        Console.WriteLine("SomeMethod called.");
    }
}

我们测试一下:

class Program
{
    static void Main()
    {
        // 创建50个线程访问单例实例
        for (int i = 0; i < 50; i++)
        {
            System.Threading.ThreadPool.QueueUserWorkItem((state) =>
            {
                Task.Delay(50).Wait();
                Singleton.Instance.SomeMethod();
            });
        }

        Console.ReadLine();
    }
}

结果:

1702434181028.png

可以发现有多个实例被创建

简单的线程安全做法


public class Singleton
{
    // 直接初始化单例实例
    private static readonly Singleton instance = new Singleton();
    public static Singleton Instance => instance;

    // 私有构造函数
    private Singleton()
    {
        // 这里可以进行一些初始化工作
        Console.WriteLine("Singleton instance created.");
    }

    public static int a = 0;
}


但是这样在我们访问如何静态字段时都会创建单例实例

测试如下:

2023-12-13 103347.png

使用 Monitor 来确保线程安全


public class Singleton
{
    private static Singleton instance;
    private static readonly object syncRoot = new object();

    // 公共属性,用于获取单例实例
    public static Singleton Instance
    {
        get
        {
            if (instance != null)
            {
                return instance;
            }

            Monitor.Enter(syncRoot);
            if (instance == null)
            {
                instance = new Singleton();
            }
            Monitor.Exit(syncRoot);
            return instance;
        }
    }

    // 私有构造函数,确保外部不能直接实例化
    private Singleton()
    {
        Console.WriteLine("Singleton instance created.");
        // 这里可以进行一些初始化工作
    }

    // 其他成员方法/属性
    public void SomeMethod()
    {
        Console.WriteLine("SomeMethod called.");
    }
}

使用 Lazy <T> 来延迟加载,Lazy <T>内部调用的创建也是线程安全的

public class Singleton
{
    // 私有静态实例变量
    private static readonly Lazy<Singleton> instance = new Lazy<Singleton>(() => new Singleton());

    // 公共属性,用于获取单例实例
    public static Singleton Instance
    {
        get
        {
            return instance.Value;
        }
    }

    // 私有构造函数,确保外部不能直接实例化
    private Singleton()
    {
        Console.WriteLine("Singleton instance created.");
        // 这里可以进行一些初始化工作
    }

    // 其他成员方法/属性
    public void SomeMethod()
    {
        Console.WriteLine("SomeMethod called.");
    }
}

这部分读者可以自行测试