c#单例模式(Singleton)的6种实现

2019-12-30 15:36:35王振洲

 以上的实现方式适用于单线程环境,因为在多线程的环境下有可能得到Singleton类的多个实例。假如同时有两个线程去判断

(null == _singleton),并且得到的结果为真,那么两个线程都会创建类Singleton的实例,这样就违背了Singleton模式“唯一实例”的初衷。

 版本二线程安全


 /// <summary>
/// A thread-safe singleton class.
/// </summary>
public sealed class Singleton
{
  private static Singleton _instance = null;
  private static readonly object SynObject = new object();

  Singleton()
  {
  }

  /// <summary>
  /// Gets the instance.
  /// </summary>
  public static Singleton Instance
  {
    get
    {
      // Syn operation.
      lock (SynObject)
      {
        return _instance ?? (_instance = new Singleton());
      }
    }
  }
}

以上方式的实现方式是线程安全的,首先我们创建了一个静态只读的进程辅助对象,由于lock是确保当一个线程位于代码的临界区时,另一个线程不能进入临界区(同步操作)。如果其他线程试图进入锁定的代码,则它将一直等待,直到该对象被释放。从而确保在多线程下不会创建多个对象实例了。只是这种实现方式要进行同步操作,这将是影响系统性能的瓶颈和增加了额外的开销。

 Double-Checked Locking

前面讲到的线程安全的实现方式的问题是要进行同步操作,那么我们是否可以降低通过操作的次数呢?其实我们只需在同步操作之前,添加判断该实例是否为null就可以降低通过操作的次数了,这样是经典的Double-Checked Locking方法。


 /// <summary>
/// Double-Checked Locking implements a thread-safe singleton class
/// </summary>
public sealed class Singleton
{
  private static Singleton _instance = null;
  // Creates an syn object.
  private static readonly object SynObject = new object();

  Singleton()
  {
  }

  public static Singleton Instance
  {
    get
    {
      // Double-Checked Locking
      if (null == _instance)
      {
        lock (SynObject)
        {
          if (null == _instance)
          {
            _instance = new Singleton();
          }
        }
      }
      return _instance;
    }
  }
}

 在介绍第四种实现方式之前,首先让我们认识什么是,当字段被标记为beforefieldinit类型时,该字段初始化可以发生在任何时候任何字段被引用之前。这句话听起了有点别扭,接下来让我们通过具体的例子介绍。


 /// <summary>
/// Defines a test class.
/// </summary>
class Test
{
  public static string x = EchoAndReturn("In type initializer");

  public static string EchoAndReturn(string s)
  {
    Console.WriteLine(s);
    return s;
  }
}

上面我们定义了一个包含静态字段和方法的类Test,但要注意我们并没有定义静态的构造函数。