c#設計模式之單例模式

  • 2019 年 11 月 2 日
  • 筆記

c#設計模式之單例模式

場景描述

單例模式對於我們來說一點也不模式,是一個常見的名稱,單例模式在程式中的實際效果就是:確保一個程式中只有一個實例,並提供一個全局訪問點,節省系統資源

單例模式無論是在實際開發中還是在軟體應用中比較常見,比如,windows系統的任務管理器、IIS的HttpApplication、實際項目中的日誌組件等等

實現方式

   單例模式為了實現一個實例,那麼只有不把實例創建暴露出去,只通過類本身來創建實例,為了實現效果,需要定義一個私有構造函數

  單例模式實現方式有:餓漢式、懶漢式、雙重驗證式、靜態內部類

  下面分別對每一種實現方式做一個簡單的實例,以及其優缺點

  餓漢式

   

   /// <summary>      /// 創建一個 Singleton 類(餓漢式)      /// 這種方式比較常用,但容易產生垃圾對象。      ///優點:沒有加鎖,執行效率會提高。      ///缺點:類載入時就初始化,浪費記憶體。      ///它基於 classloder 機制避免了多執行緒的同步問題,不過,instance 在類裝載時就實例化,      ///雖然導致類裝載的原因有很多種,在單例模式中大多數都是調用 getInstance 方法,      ///但是也不能確定有其他的方式(或者其他的靜態方法)導致類裝載,這時候初始化 instance 顯然沒有達到 lazy loading 的效果。      /// </summary>      public class SingleObject      {          //創建 SingleObject 的一個對象          private static SingleObject instance = new SingleObject();            //讓構造函數為 private,這樣該類就不會被實例化          private SingleObject() {              Console.WriteLine("我被創建了.餓漢式");          }            //獲取唯一可用的對象          public static SingleObject GetInstance()          {              return instance;          }            public void ShowMessage()          {              Console.WriteLine("Hello World.餓漢式");          }      }

 

  懶漢式

    

  /// <summary>      /// 創建一個 Singleton 類(懶漢式)      /// 這種方式具備很好的 lazy loading,能夠在多執行緒中很好的工作,但是,效率很低,99% 情況下不需要同步。      /// 優點:第一次調用才初始化,避免記憶體浪費。      /// 缺點:懶漢式在單個執行緒中沒有問題,但多個執行緒同事訪問的時候就可能同事創建多個實例,而且這多個實例不是同一個對象。      /// </summary>      public class SingleObject1      {          //創建 SingleObject 的一個對象          private static SingleObject1 instance;            //讓構造函數為 private,這樣該類就不會被實例化          private SingleObject1() { }            //獲取唯一可用的對象          public static SingleObject1 GetInstance()          {              if (instance == null)              {                  instance = new SingleObject1();                  Console.WriteLine("我被創建了.懶漢式");              }              return instance;          }            public void ShowMessage()          {              Console.WriteLine("Hello World.懶漢式");          }      }

 

  雙重驗證式

 

    /// <summary>      /// 創建一個 Singleton 類(雙重驗證)      /// 這種方式具備很好的 lazy loading,能夠在多執行緒中很好的工作,但是,效率很低,99% 情況下不需要同步。      /// 優點:第一次調用才初始化,避免記憶體浪費,執行緒安全。      /// 缺點:必須加鎖 synchronized 才能保證單例,但加鎖會影響效率。      /// </summary>      public class SingleObject2      {          //創建 SingleObject 的一個對象          private static SingleObject2 instance;            // 定義一個標識確保執行緒同步          private static readonly object locker = new object();              //讓構造函數為 private,這樣該類就不會被實例化          private SingleObject2() { }            //獲取唯一可用的對象          public static SingleObject2 GetInstance()          {              //// 如果為空,那麼就加鎖,創建實例              if (instance == null)              {                  lock (locker)                  {                      //// 枷鎖成功後,在做一次非空判斷,避免在加鎖期間以創建了實例而導致重複創建                      if (instance == null)                      {                          instance = new SingleObject2();                          Console.WriteLine("我被創建了.雙重驗證");                      }                  }              }              return instance;          }            public void ShowMessage()          {              Console.WriteLine("Hello World.雙重驗證");          }      }

 

 

  靜態內部類

   

  /// <summary>      /// 創建一個 Singleton 類(靜態內部類)      /// 這種方式不用加鎖,在效率上和記憶體使用上都比較優秀      /// 克服了餓漢模式的不足餓漢模式執行效率高,由於在類載入的時候初始化導致記憶體浪費      /// </summary>      public class SingletonStatic      {          /// <summary>          /// 內部類          /// </summary>          public class SingletonStaticInner          {              /// <summary>              /// 當一個類有靜態構造函數時,它的靜態成員變數不會被beforefieldinit修飾              /// 就會確保在被引用的時候才會實例化,而不是程式啟動的時候實例化              /// </summary>              static SingletonStaticInner() { }                /// <summary>              /// 實例化              /// </summary>              internal static SingletonStatic singletonStatic = new SingletonStatic();          }            /// <summary>          /// 私有構造函數          /// </summary>          private SingletonStatic() {              Console.WriteLine("我被創建了.靜態內部類");          }            /// <summary>          /// 獲取實例          /// </summary>          /// <returns></returns>          public static SingletonStatic GetInstance()          {              return SingletonStaticInner.singletonStatic;          }            public void ShowMessage()          {              Console.WriteLine("Hello World.靜態內部類");          }      }

 

  每一種創建方式測試

   創建一個控制台程式,通過多執行緒對每一種實現方式使用,查看其實例次數分析:

/*
介紹
意圖:保證一個類僅有一個實例,並提供一個訪問它的全局訪問點。
主要解決:一個全局使用的類頻繁地創建與銷毀。
何時使用:當您想控制實例數目,節省系統資源的時候。
如何解決:判斷系統是否已經有這個單例,如果有則返回,如果沒有則創建。
關鍵程式碼:構造函數是私有的。
應用實例:
典型的已有應用:
1、windows的任務管理器等
2、IIS的HttpApplication,所有的HttpModule都共享一個HttpApplication實例
在項目中的實際使用場景:
1、日誌組件
2、多執行緒執行緒池管理
3、網站計數器
4、配置文件管理
*/

  
class Program { static void Main(string[] args) { TaskFactory taskFactory = new TaskFactory(); List<Task> taskList = new List<Task>(); //// 測試--餓漢式 for (int i = 0; i < 5; i++) { taskList.Add(taskFactory.StartNew(() => { SingleObject.GetInstance(); })); } //// 測試--懶漢式 for (int i = 0; i < 5; i++) { taskList.Add(taskFactory.StartNew(() => { SingleObject1.GetInstance(); })); } //// 測試--雙重驗證 for (int i = 0; i < 5; i++) { taskList.Add(taskFactory.StartNew(() => { SingleObject2.GetInstance(); })); } //// 測試--靜態內部類 for (int i = 0; i < 5; i++) { taskList.Add(taskFactory.StartNew(() => { SingletonStatic.GetInstance(); })); } Console.ReadLine(); } }

 

 

  運行結果:

  

  通過結果可以看出:懶漢式實際創建了2個實例,所以在多執行緒中,懶漢式有執行緒不安全問題

總結

  根據單例模式是每一種實現方式對比分析,在實際使用過程中:

  如果是單執行緒應用環境,建議可以採用懶漢模

  如果是多執行緒應用環境,建議採用靜態內部類方式

 

Tags: