Singleton Pattern (单例模式) (线程安全/lock/锁)

创建时间:
2014-09-27 09:09
最近更新:
2018-10-10 11:32

Resource

  1. MSDN: Implementing Singleton in C#) - 2018-08-21 仅备份、待阅读
  1. 在 C# 中实现 Singleton
  2. 在 C# 中实现 Singleton
  3. 在 C# 中实现 Singleton
  4. 单例模式 (Singleton) 的 6 种实现
  5. 汤姆大叔 - 大叔手记 (10): 别再让面试官问你单例 (暨 6 种实现方式让你堵住面试官的嘴)
  6. 深入解析单例模式

最简 单例模式 的实现: 利用 静态类/静态字段 的特性

  1. 详见本站《C# - 构造函数》之 "private 空构造函数示例"。
  2. System.Web.Mvc.EmptyResult 类 与 System.Linq.EmptyEnumerable<TElement> 类 均采用此方式实现了单例。详见其源码。

Tony 整理的单例模式实现代码

//Double-Checked Locking implements a thread-safe Singleton class:
//单例模式 的类不应该被继承,否则每个子类都可以创建实例,这就违背了 单例模式 "唯一实例" 的初衷。
//当对一个类应用 sealed 修饰符时,此修饰符会阻止其他类从该类继承。没有要求 单例模式 的类 一定要定义为 sealed,只是为了保险起见可以使用 sealed 显式定义为不允许派生。
public sealed class Example0
{
    //私有静态全局变量,用于保存该类的唯一实例:
    private static volatile Example0 _singleton;

    //仅供本类中 lock 使用:
    private static readonly object _syncRoot = new Object();

    //私有无参构造函数用于禁止在该类之外创建该类的实例,同时也会阻止其他类从该类继承:
    private Example0() { }

    //全局访问点,在该类之外无需实例化该类,即可调用本属性:
    public static Example0 Singleton
    {
        get {
            //Double-Checked Locking
            //第 1 次 check 在 lock 前,使 lock 的次数尽可能少,避免性能损失:
            if(_singleton == null) {
                //如同时有两个线程执行下一行 if(_singleton == null) 而且结果为 true,则两个线程都会创建 Example0 类的实例,这样就违背了 单例模式 "唯一实例" 的初衷。此处使用 lock 关键字可确保当一个线程位于代码的临界区时,另一个线程不会进入该临界区。如果其他线程试图进入锁定的代码,则它将一直等待 (即被阻止),直到该对象被释放。从而确保在多线程下不会创建多个对象实例。但 lock 将导致同步操作,这将是影响系统性能的瓶颈,同时增加了额外的开销,因此需要上一行 check。
                lock(_syncRoot) {
                    //第 2 次 check 在 lock 内,仅当为 null 时才创建实例:
                    if(_singleton == null) {
                        //仅在首次调用时实例化:
                        _singleton = new Example0();
                    }
                }
            }
            return _singleton;
        }
    }

}

基于以上实现的生产代码:

using Tl.CryptographyPlus;

namespace T.CMS.BLL.Security
{
    public sealed class IdSecurity_ArticleId
    {
        private static volatile IdSecurity _singleton;
        private static readonly object _syncRoot = new Object();
        private IdSecurity_ArticleId() { }
        public static IdSecurity Singleton
        {
            get
            {
                if(_singleton == null) {
                    lock(_syncRoot) {
                        if(_singleton == null) {
                            byte salt = 123;
                            string charSet = "3UR4NSJBDHTAVP5K68XGEW9F7QCMY";
                            _singleton = new IdSecurity(salt, charSet);
                        }
                    }
                }
                return _singleton;
            }
        }
    }
}