C#设计模式之Singleton模式

2020-06-11 18:00:23王旭

线程安全的单例模式

Lock版本

提到线程安全,很多同学第一反应就是用lock,不错,lock是个可行的办法,让我们试试。添加一个引用类型的对象作为lock对象,修改代码如下(什么?你问我为什必须是引用类型的对象而不能是值类型的对象?因为lock的时候,如果对象是值类型,那么lock仅仅锁住了它的一个副本,另外一个线程可以畅通无阻的再次lock,这样lock就失去了阻塞线程的意义)

	private static object _SyncObj = new object();
  public static Singleton Instance
  {
   get
   {
    lock (_SyncObj)
    {
     if (_Instance == null)
     {
      _Instance = new Singleton();
     }
     return _Instance;
    }    
   }
  }

运行一下,输出

只有一个实例创建,证明Lock起作用了,这个模式可行!不过有些不喜欢用Lock的同学可能要问,还有没有其他办法呢?答案是有的。

静态构造器版本

回想一下,C#中的类静态构造器,只会在这个类第一次被使用的时候调用一次,天然的线程安全,那我们试试不用Lock使用类静态构造器?修改Singleton类如下:

 class Singleton
 {
  private static Singleton _Instance = null;
  private Singleton()
  {
   Console.WriteLine("Created");
  }

  static Singleton()
  {
   _Instance = new Singleton();
  }

  //private static object _SyncObj = new object();
  public static Singleton Instance
  {
   get { return _Instance; }
  }

  public void DumbMethod()
  {

  }
 }

去掉了Lock,添加了一个类静态构造器,试一试。

完美!对于不喜欢用Lock(在这个例子中,实例只会创建一次但是之后的所有线程都要先排队Lock再进入Critical code进行检查,效率比较低下)的同学,类静态构造器提供了一种很好的选择。
不过俗话说,人心苦不足 , 我们总是追求卓越。这个版本比Lock版本似乎更好一点,那还有没有更好的版本呢?有的。

Lazy版本

从net 4.0开始,C#开始支持延迟初始化,通过Lazy关键字,我们可以声明某个对象为仅仅当第一次使用的时候,再初始化,如果一直没有调用,那就不初始化,省去了一部分不必要的开销,提升了效率。如果你不熟悉Lazy或者想更多了解它,请参考。我们今天关注的重点在于,Lazy也是天生线程安全的,所以我们尝试用它来实现Singleton模式?修改代码如下:

 class Singleton
 {
  private static Lazy<Singleton> _Instance = new Lazy<Singleton>(() => new Singleton());
  private Singleton()
  {
   Console.WriteLine("Created");
  }

  public static Singleton Instance
  {
   get
   {
    return _Instance.Value;
   }
  }

  public void DumbMethod()
  {

  }
 }