本筆記摘抄自:https://www.cnblogs.com/PatrickLiu/p/8250985.html,記錄一下學習程序以備后續查用,
一、引言
設計模式的分類:
1)依目的:
創建型(Creational)模式:負責物件創建
結構型(Structural)模式:處理類與物件間的組合
行為型(Behavioral)模式:類與物件互動中的職責分配
2)依范圍:
類模式:處理類與子類的靜態關系
物件模式:處理物件間的動態關系
注:本系列文章依目的分類來進行,
二、單例模式的介紹
單例模式:英文名稱--Singleton Pattern;分類--創建型;定義--一個類僅有一個實體,
2.1、動機(Motivate)
在軟體系統中,經常有這樣一些特殊的類,必須保證它們在系統中只存在一個實體,才能確保它們的邏輯正確性、以及良好的效率,
如何繞過常規的構造器,提供一種機制來保證一個類只有一個實體?這應該是類設計者的責任,而不是使用者的責任,
2.2、意圖(Intent)
保證一個類僅有一個實體,并提供一個該實體的全域訪問點,--《設計模式GoF》
2.3、結構圖(Structure)

2.4、模式組成
這個模式里面只有一個型別,就是Singleton型別,并且這個類只有一個實體,可以通過Instance()方法獲取該型別的實體,
2.5、代碼實作
既然是單實體,肯定會涉及到多執行緒的問題,
2.5.1單執行緒Singleton模式的實作
class Program { /// <summary> /// 單例模式的實作 /// </summary> public sealed class Singleton { //定義一個靜態變數來保存類的實體 private static Singleton uniqueInstance; //定義私有建構式,使外界不能創建該類實體, private Singleton() { Console.WriteLine("Singleton物件已被創建,"); } /// <summary> /// 定義公有方法提供一個全域訪問點,也可以定義公有屬性來提供全域訪問點, /// </summary> /// <returns></returns> public static Singleton GetInstance() { //如果類的實體不存在則創建,否則直接回傳, if (uniqueInstance == null) { uniqueInstance = new Singleton(); } return uniqueInstance; } } static void Main(string[] args) { #region 單例模式 var singleton = Singleton.GetInstance(); Console.Read(); #endregion } }View Code
運行結果如下:

私有的實體構造器是屏蔽外界的呼叫,上面的單例模式的實作在單執行緒下確實是完美的,也很好的滿足了我們單執行緒環境的需求,
在多執行緒環境下,使用Singleton模式仍然有可能得到Singleton類的多個實體物件,因為在兩個執行緒同時運行GetInstance方法時,
此時兩個執行緒判斷(uniqueInstance==null)這個條件時都回傳真,此時兩個執行緒就都會創建Singleton的實體,
2.5.2多執行緒Singleton模式的實作
class Program { /// <summary> /// 單例模式的實作 /// </summary> public sealed class Singleton { //定義一個靜態變數來保存類的實體 private static volatile Singleton uniqueInstance; //定義一個標識確保執行緒同步 private static readonly object locker = new object(); //定義私有建構式,使外界不能創建該類實體, private Singleton() { Console.WriteLine("Singleton物件已被創建,"); } /// <summary> /// 定義公有方法提供一個全域訪問點,也可以定義公有屬性來提供全域訪問點, /// </summary> /// <returns></returns> public static Singleton GetInstance() { //當第一個執行緒運行到這里時,此時會對locker物件"加鎖", //當第二個執行緒運行該方法時,首先檢測到locker物件為"加鎖"狀態,該執行緒就會掛起等待第一個執行緒解鎖, //lock陳述句運行完之后(即執行緒運行完之后)會對該物件"解鎖", lock (locker) { // 如果類的實體不存在則創建,否則直接回傳 if (uniqueInstance == null) { uniqueInstance = new Singleton(); } } return uniqueInstance; } } static void Main(string[] args) { #region 單例模式 for (int i = 1; i <= 10; i++) { Thread thread = new Thread(new ParameterizedThreadStart(Worker)); thread.Start(i); } Console.Read(); #endregion } private static void Worker(object parameter) { Console.WriteLine($"Thread {parameter} is running."); Singleton.GetInstance(); } }View Code
運行結果如下:

上面的解決方案確實可以解決多執行緒的問題,但是上面代碼每個執行緒都會對執行緒輔助物件locker加鎖之后再判斷實體是否存在,這個是完全沒有必要的,
因為當第一個執行緒創建了該類的實體之后,后面的執行緒此時只需要直接判斷(uniqueInstance==null)為假即可,從而減少額外的開銷以提高性能,
為了改進上面實作方式的缺陷,我們只需要在lock陳述句前面加一句(uniqueInstance==null)的判斷,這種雙層if加lock的實作方式,我們稱它為
“雙重鎖定(Double Check)”,
class Program { /// <summary> /// 單例模式的實作 /// </summary> public sealed class Singleton { //定義一個靜態變數來保存類的實體 private static volatile Singleton uniqueInstance; //定義一個標識確保執行緒同步 private static readonly object locker = new object(); //定義私有建構式,使外界不能創建該類實體, private Singleton() { Console.WriteLine("Singleton物件已被創建,"); } /// <summary> /// 定義公有方法提供一個全域訪問點,也可以定義公有屬性來提供全域訪問點, /// </summary> /// <returns></returns> public static Singleton GetInstance() { //當第一個執行緒運行到這里時,此時會對locker物件"加鎖", //當第二個執行緒運行該方法時,首先檢測到locker物件為"加鎖"狀態,該執行緒就會掛起等待第一個執行緒解鎖, //lock陳述句運行完之后(即執行緒運行完之后)會對該物件"解鎖", //雙重鎖定只需要一句判斷就可以了 if (uniqueInstance == null) { lock (locker) { // 如果類的實體不存在則創建,否則直接回傳 if (uniqueInstance == null) { uniqueInstance = new Singleton(); } } } return uniqueInstance; } } static void Main(string[] args) { #region 單例模式 for (int i = 1; i <= 10; i++) { Thread thread = new Thread(new ParameterizedThreadStart(Worker)); thread.Start(i); } Console.Read(); #endregion } private static void Worker(object parameter) { Console.WriteLine($"Thread {parameter} is running."); Singleton.GetInstance(); } }View Code
volatile修飾:編譯器在編譯代碼的時候會對代碼的順序進行微調,用volatile修飾保證了嚴格意義的順序,一個定義為volatile的變數是說這變數可能會
被意想不到地改變,這樣,編譯器就不會去假設這個變數的值了,精確地說就是,優化器在用到這個變數時必須每次都小心地重新讀取這個變數的值,而
不是使用保存在暫存器里的備份,
三、C#中實作了單例模式的類
現在我們看看,如何使用C#語言的特性來實作單例的Singleton模式,
//Singleton模式的實作 public sealed class Singleton { public static readonly Singleton instance = new Singleton(); private Singleton() { } } //以上是行內初始化(生成的同時進行初始化)的單例模式,它等同于: public sealed class Singleton { public static readonly Singleton instance; //靜態建構式,CLR只執行一次, static Singleton() { instance = new Singleton(); } //私有建構式,防止外界呼叫 private Singleton() { } }
行內初始化其實是把靜態的欄位放到靜態構造器去初始化,只要想訪問靜態欄位,必定已經在使用之前先執行靜態構造器,這樣能夠精確地保證使用
的時候一定能拿到實體,如果不使用也不會實體化物件,這也就是延時加載的功能,它同樣能夠支持多執行緒環境,因為只可能有一個執行緒執行靜態構造
器,不存在多個執行緒去執行靜態構造器(感覺就是程式已經自動為我們加鎖了),
它的一點弊端就是:靜態構造器只能宣告為一個私有的、無引數的構造器,因而不支持引數化的實體化方法,
需要說明的是:HttpContext.Current就是一個單例,它們是通過Singleton的擴展方式來實作的,
四、Singleton模式的擴展
1)將一個實體擴展到n個實體,例如物件池的實作,(n不是指無限個實體,而是固定的某個數,)
2)將new構造器的呼叫轉移到其他類中,例如多個類協同作業環境中,某個區域環境只需要擁有某個類的一個實體,
3)理解和擴展Singleton模式的核心是“如何控制用戶使用new對一個類的實體構造器的任意呼叫”,
五、單例模式的實作要點
1)Singleton模式是限制而不是改進類的創建,
2)Singleton類中的實體構造器可以設定為Protected以允許子類派生,
3)Singleton模式一般不要支持Icloneable介面,因為這可能導致多個物件實體,與Singleton模式的初衷違背,
4)Singleton模式一般不要支持序列化,這也有可能導致多個物件實體,這也與Singleton模式的初衷違背,
5)Singleton只考慮物件創建的管理,沒有考慮銷毀的管理,為什么這樣做呢?因為Net平臺是支持垃圾回收的,所以我們一般沒有必要對其進行銷毀
處理,
6)理解和擴展Singleton模式的核心是“如何控制用戶使用new對一個類的構造器的任意呼叫”,
7)可以很簡單的修改一個Singleton,使它有少數幾個實體,這樣做是允許的而且是有意義的,
5.1、單例模式的優點
1)實體控制:Singleton會阻止其他物件實體化其自己的Singleton物件的副本,從而確保所有物件都訪問唯一實體,
2)靈活性:因為類控制了實體化程序,所以類可以更加靈活修改實體化程序,
5.2、單例模式的缺點
1)開銷:雖然數量很少,但如果每次物件請求參考時都要檢查是否存在類的實體,將仍然需要一些開銷,可以通過使用靜態初始化解決此問題,
2)可能的開發混淆:使用Singleton物件(尤其在類別庫中定義的物件)時,開發人員必須記住自己不能使用new關鍵字實體化物件,因為可能無
法訪問庫源代碼,因此應用程式開發人員可能會意外發現自己無法直接實體化此類,
3)物件的生存期:Singleton不能解決洗掉單個物件的問題,因為它包含對該靜態的私有欄位的參考,靜態欄位是不能被CLR回收記憶體的,該實
例會和應用程式生命周期一樣長,一直存在,
5.3、單例模式的使用場合
1)當類只能有一個實體而且客戶可以從一個眾所周知的訪問點訪問它時,
2)當這個唯一實體應該是通過子類化可擴展的,并且客戶應該無需更改代碼就能使用一個擴展的實體時,
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/82985.html
標籤:C#
上一篇:C#實作的對檔案的重命名
