c#設計模式之單例模式
場景描述
單例模式對于我們來說一點也不模式,是一個常見的名稱,單例模式在程式中的實際效果就是:確保一個程式中只有一個實體,并提供一個全域訪問點,節省系統資源
單例模式無論是在實際開發中還是在軟體應用中比較常見,比如,windows系統的任務管理器、IIS的HttpApplication、實際專案中的日志組件等等
實作方式
單例模式為了實作一個實體,那么只有不把實體創建暴露出去,只通過類本身來創建實體,為了實作效果,需要定義一個私有建構式
單例模式實作方式有:餓漢式、懶漢式、雙重驗證式、靜態內部類、延遲加載(Lazy)
下面分別對每一種實作方式做一個簡單的實體,以及其優缺點
餓漢式
/// <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.靜態內部類"); } }
延遲加載(Lazy)
/// <summary> /// 創建一個 Singleton 類(Lazy) /// 該方式是需要.netformwork4+ /// </summary> public class SingletonLazy { private static Lazy<SingletonLazy> singletonLazy = new Lazy<SingletonLazy>(()=>new SingletonLazy()); /// <summary> /// 私有建構式 /// </summary> private SingletonLazy() { Console.WriteLine("我被創建了.Lazy"); } /// <summary> /// 獲取實體 /// </summary> /// <returns></returns> public static SingletonLazy GetInstance() { return singletonLazy.Value; } public void ShowMessage() { Console.WriteLine("Hello World.Lazy"); } }
每一種創建方式測驗
創建一個控制臺程式,通過多執行緒對每一種實作方式使用,查看其實體次數分析:
/* 介紹意圖:保證一個類僅有一個實體,并提供一個訪問它的全域訪問點,主要解決:一個全域使用的類頻繁地創建與銷毀,何時使用:當您想控制實體數目,節省系統資源的時候,如何解決:判斷系統是否已經有這個單例,如果有則回傳,如果沒有則創建,關鍵代碼:建構式是私有的,應用實體:典型的已有應用: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(); })); } //// 測驗--Lazy for (int i = 0; i < 5; i++) { taskList.Add(taskFactory.StartNew(() => { SingletonLazy.GetInstance(); })); } Console.ReadLine(); } }
運行結果:
總結
根據單例模式是每一種實作方式對比分析,在實際使用程序中:建議采用延遲加載(Lazy)
當然,還有其他類似的實作單例的方式,沒有寫到的,也歡迎大家一起交流,勿噴
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/8143.html
標籤:ASP.NET
上一篇:正則運算式初探
下一篇:C#設計模式之策略模式
