定义

单例指的是只能存在一个实例的类(在C#中,更准确的说法是在每个AppDomain之中只能存在一个实例的类,它是软件工程中使用最多的几种模式之一。在第一个使用者创建了这个类的实例之后,其后需要使用这个类的就只能使用之前创建的实例,无法再创建一个新的实例。通常情况下,单例会在第一次被使用时创建。

特点

  • 单例类都只有一个private的无参构造函数
  • 类声明为sealed(不是必须的)
  • 类中有一个静态变量保存着所创建的实例的引用
  • 单例类会提供一个静态方法或属性来返回创建的实例的引用(eg.GetInstance)

    实现

    非线程安全

    这种方法不是线程安全的,会存在两个线程同时执行if (instance == null)并且创建两个不同的instance,后创建的会替换掉新创建的,导致之前拿到的reference为空。

    1. //Bad code! Do not use!
    2. public sealed class Singleton
    3. {
    4. private static Singleton instance = null;
    5. private Singleton()
    6. {
    7. }
    8. public static Singleton instance
    9. {
    10. get
    11. {
    12. if (instance == null)
    13. {
    14. instance = new Singleton();
    15. }
    16. return instance;
    17. }
    18. }
    19. }

    简单的线程安全实现

    相比较于实现一,这个版本加上了一个对instance的锁,在调用instance之前要先对padlock上锁,这样就避免了实现一中的线程冲突,该实现自始至终只会创建一个instance了。但是,由于每次调用Instance都会使用到锁,而调用锁的开销较大,这个实现会有一定的性能损失。
    注意这里我们使用的是新建一个private的object实例padlock来实现锁操作,而不是直接对Singleton进行上锁。直接对类型上锁会出现潜在的风险,因为这个类型是public的,所以理论上它会在任何code里调用,直接对它上锁会导致性能问题,甚至会出现死锁情况。
    NoteC#中,同一个线程是可以对一个object进行多次上锁的,但是不同线程之间如果同时上锁,就可能会出现线程等待,或者严重的会出现死锁情况。因此,我们在使用lock时,尽量选择类中的私有变量上锁,这样可以避免上述情况发生。

    1. public sealed class Singleton
    2. {
    3. private static Singleton instance = null;
    4. private static readonly object padlock = new object();
    5. Singleton()
    6. {
    7. }
    8. public static Singleton Instance
    9. {
    10. get
    11. {
    12. lock (padlock)
    13. {
    14. if (instance == null)
    15. {
    16. instance = new Singleton();
    17. }
    18. return instance;
    19. }
    20. }
    21. }
    22. }

    双重验证线程安全实现

    在保证线程安全的同时,这个实现还避免了每次调用Instance都进行lock操作,这会节约一定的时间。 程序员在自己实现时很容易出错。如果对这个模式的代码进行自己的修改,要倍加小心,因为double check的逻辑较为复杂,很容易出现思考不周而出错的情况。

    1. public sealed calss Singleton
    2. {
    3. private static Singleton instance = null;
    4. private static readonly object padlock = new object();
    5. Singleton()
    6. {
    7. }
    8. public static Singleton Instance
    9. {
    10. get
    11. {
    12. if (instance == null)
    13. {
    14. lock (padlock)
    15. {
    16. if (instance == null)
    17. {
    18. instance = new Singleton();
    19. }
    20. }
    21. }
    22. return instance;
    23. }
    24. }
    25. }

    完全延迟加载实现(fully lazy instantiation)

    确保了instance只会在Instance的get方法里面调用,且只会在第一次调用前初始化。

    1. public sealed class Singleton
    2. {
    3. private Singleton()
    4. {
    5. }
    6. public static Singleton Instance
    7. {
    8. get
    9. {
    10. return Nested.instance;
    11. }
    12. }
    13. private class Nested
    14. {
    15. // Explicit static constructor to tell C# compiler
    16. // not to mark type as beforefieldinit
    17. static Nested()
    18. {
    19. }
    20. internal static readonly Singleton instance = new Singleton();
    21. }
    22. }

    使用.NET4的Lazy类型

    1. public sealed class Singleton
    2. {
    3. private static readonly Lazy<Singleton> lazy = new Lazy<Singleton>(() => new Singleton());
    4. public static Singleton Instance
    5. {
    6. get
    7. {
    8. return lazy.Value;
    9. }
    10. }
    11. private Singleton()
    12. {
    13. }
    14. }