仓库源文站点原文


title: 单例模式(Singleton Pattern) date: 2020-03-29 20:25:58 tags:


单例模式的目的是:

确保类只有一个实例并提供全局访问。

<!-- more -->

实现

首先是最简单的单例模式的代码:

namespace DesignPattern.SingletonPattern
{
    /// <summary>
    /// 单例
    /// </summary>
    public class Singleton
    {
        /// <summary>
        /// 记录唯一实例的静态变量
        /// </summary>
        private static Singleton uniqueInstance;

        /// <summary>
        /// 私有的构造函数
        /// </summary>
        private Singleton() { }

        /// <summary>
        /// 获取实例
        /// </summary>
        /// <returns>唯一的 Singleton 实例</returns>
        public static Singleton getInstance()
        {
            if (uniqueInstance == null)
                uniqueInstance = new Singleton();
            return uniqueInstance;
        }
    }
}

Singleton 的构造方法被定义成了 private,这代表无法在 Singleton 类的外部实例化 Singleton 类。

getInstance 方法被设置为 static,这样在 Singleton 类外部即使不实例化 Singleton 类也可以通过 Singleton.getInstance() 调用此方法。

这样,只要在 getInstance 方法中进行简单的判断,就可以实现从始至终只有一个 Singleton 类的实例。

多线程

然而,在多线程时就会出现问题:两个线程在运行时,可能会同时出现 (uniqueInstance == null) 返回为真的情况,这就导致会实例化一个以上的 Singleton 类的情况出现。

所以,我们需要将其变成同步方法,在 C# 中可以用以下两种方法实现:

第一是使用 [MethodImpl(MethodImplOptions.Synchronized)] 属性:

using System.Runtime.CompilerServices;

namespace DesignPattern.SingletonPattern
{
    /// <summary>
    /// 单例
    /// </summary>
    public class Singleton
    {
        /// <summary>
        /// 记录唯一实例的静态变量
        /// </summary>
        private static Singleton uniqueInstance;

        /// <summary>
        /// 私有的构造函数
        /// </summary>
        private Singleton() { }

        /// <summary>
        /// 获取实例
        /// </summary>
        /// <returns>唯一的 Singleton 实例</returns>
        [MethodImpl(MethodImplOptions.Synchronized)]
        public static Singleton getInstance()
        {
            if (uniqueInstance == null)
                uniqueInstance = new Singleton();
            return uniqueInstance;
        }
    }
}

第二种是使用 lock

namespace DesignPattern.SingletonPattern
{
    /// <summary>
    /// 单例
    /// </summary>
    public class Singleton
    {
        /// <summary>
        /// 记录唯一实例的静态变量
        /// </summary>
        private static Singleton uniqueInstance;

        /// <summary>
        /// lock 标识
        /// </summary>
        private static readonly object locker = new object();

        /// <summary>
        /// 私有的构造函数
        /// </summary>
        private Singleton() { }

        /// <summary>
        /// 获取实例
        /// </summary>
        /// <returns>唯一的 Singleton 实例</returns>
        public static Singleton getInstance()
        {
            lock (locker)
            {
                if (uniqueInstance == null)
                    uniqueInstance = new Singleton();
            }
            return uniqueInstance;
        }
    }
}

上面两种方法都可以解决在单例模式下多线程的问题,但是如果你在程序中频繁用到 Singleton.getInstance() 将会大大的使性能降低,因为同步一个方法会使程序执行效率下降,所以我们可以在这种情况下使用如下方法:

namespace DesignPattern.SingletonPattern
{
    /// <summary>
    /// 单例
    /// </summary>
    public class Singleton
    {
        /// <summary>
        /// 记录唯一实例的静态变量
        /// </summary>
        private static Singleton uniqueInstance = new Singleton();

        /// <summary>
        /// 私有的构造函数
        /// </summary>
        private Singleton() { }

        /// <summary>
        /// 获取实例
        /// </summary>
        /// <returns>唯一的 Singleton 实例</returns>
        public static Singleton getInstance()
        {
            return uniqueInstance;
        }
    }
}

可以看到,我们在声明 uniqueInstance 的时候直接实例化了 Singleton 类。

这样在程序开始的时候这个唯一实例就已经存在了。避免了同步方法,也就解决了多线程的问题。

注意的点

代码

SingletonPattern