设计模式 – 18)单例模式

  • A+
所属分类:.NET技术
摘要

多个线程同时访问 Singleton 的 GetInstance() 方法,可能会造成创建多个实例;
比如 A 先进入后在准备实例化 Singleton 的过程中,B 也进入了,这时 _singleton 还是为 null,然后 B 也去实例化 Singleton 了。

class STWindow {     static Window win;     private STWindow() { }              public static Window GetInstance()     {         if (win == null || !win.IsVisible)         {             win = new Window();             win.Height = 200;             win.Width = 200;         }                              return win;     }         }  class Singleton {     static Singleton _singleton;     private Singleton() { }      public static Singleton GetInstance()     {         if (_singleton == null)         {             _singleton = new Singleton();         }         return _singleton;     } } 

1. 多线程同时创建实例问题

多个线程同时访问 Singleton 的 GetInstance() 方法,可能会造成创建多个实例;
比如 A 先进入后在准备实例化 Singleton 的过程中,B 也进入了,这时 _singleton 还是为 null,然后 B 也去实例化 Singleton 了。

class Singleton {     static Singleton _singleton;     private Singleton() { }      public static Singleton GetInstance()     {         if (_singleton == null)         {             Console.WriteLine("实例化一个 Singleton");             _singleton = new Singleton();         }         Console.WriteLine("返回一个 Singleton");         return _singleton;     } }  System.Threading.Thread a = new System.Threading.Thread(() => {     Singleton.GetInstance(); });  System.Threading.Thread b = new System.Threading.Thread(() => {     Singleton.GetInstance(); });  System.Threading.Thread c = new System.Threading.Thread(() => {     Singleton.GetInstance();     });  a.Start(); b.Start(); c.Start(); 
实例化一个 Singleton 实例化一个 Singleton 返回一个 Singleton 返回一个 Singleton 返回一个 Singleton 

我们通过加个锁来解决这个问题。

public static readonly object syncRoot = new object();  public static Singleton GetInstance() {     lock (syncRoot)     {         if (_singleton == null)         {             Console.WriteLine("实例化一个 Singleton");             _singleton = new Singleton();         }     }                  Console.WriteLine("返回一个 Singleton");     return _singleton; } 
实例化一个 Singleton 返回一个 Singleton 返回一个 Singleton 线程 0x1a08 已退出,返回值为 0 (0x0)。 返回一个 Singleton 线程 0x45b4 已退出,返回值为 0 (0x0)。 线程 0x5a04 已退出,返回值为 0 (0x0)。 

2. 每次实例都要锁的性能问题

使用双重锁定,只有未实例化时才锁,锁完判断是否还未实例化。

public static Singleton GetInstance() {     if (_singleton == null)     {         lock (syncRoot)         {             if (_singleton == null)             {                 _singleton = new Singleton();             }         }     }     return _singleton; } 

3.饿汉式单例

class Singleton {     static readonly Singleton _instance = new Singleton();      private Singleton() { }      public static Singleton GetInstance()     {                     return _instance;     } } 

这种静态初始化的方式是自己被加载时就将自己实例化,因此被称为饿汉式单例;
而第一次被引用时才将自己实例化的方式被称为懒汉式单例。C# 中饿汉式已足够满足。