標籤:
單例模式:保證一個類僅有一個執行個體,並提供一個訪問它的全域訪問點。
通常我們可以讓一個全域變數使得同一個對象被訪問,但是它不能防止你執行個體化多個對象,一個最好的辦法就是,讓類自身負責儲存它的唯一執行個體。這個類可以保證沒有其它執行個體可以被建立,並且它可以提供一個訪問該執行個體的方法。
舉例如下:
/// <summary> /// 普通的單例模式 /// 通過將建構函式私人化,並提供私人靜態變數和靜態方法達到對執行個體化的控制 /// 這種方式產生的對象是在使用時才被執行個體化,因此被稱為懶漢式 /// </summary> class CommonSingleton { private static CommonSingleton instance; private CommonSingleton() { } public static CommonSingleton GetInstance() { if (instance == null) { instance = new CommonSingleton(); } return instance; } }
用戶端調用:
CommonSingleton commonSingleton1 = CommonSingleton.GetInstance(); CommonSingleton commonSingleton2 = CommonSingleton.GetInstance(); if (commonSingleton1==commonSingleton2) { Console.WriteLine("commonSingleton is the same one."); }
運行結果為: commonSingleton is the same one.
在多線程的程式中,多個線程同時訪問Singleton的GetInstance()放法,還是有可能造成建立多個執行個體,這是需要引入鎖(lock)機制,lock是確保當一個線程位於代碼的臨界區時,另一個線程不進入臨界區,如果其他線程試圖進入鎖定的代碼,則它將一直等待,直到該對象被釋放,舉例如下:
class LockSingleton { private static LockSingleton instance; //程式運行時建立的一個靜態唯讀進程輔助對象 private static readonly object syncRoot = new object(); private LockSingleton() { } public static LockSingleton GetInstance() { lock (syncRoot) { //同一個時刻,這部分程式只有一個線程可以進入 if (instance == null) { instance = new LockSingleton(); } return instance; } } }
這段代碼使得對象的建立由最先進入的那個線程建立,以後的線程在進入時不會再建立對象執行個體了。
但是每次調用GetInstance方法時都需要Lock,即線程直線可能會被阻塞,這會影響效能,更好的辦法是採用雙重鎖定,舉例如下:
/// <summary> /// Double-Check Locking /// 對鎖定方法的改進,僅在執行個體為null時加鎖,提高了代碼的效率 /// </summary> class DoubleCheckLockSingleton { private static DoubleCheckLockSingleton instance; private static readonly object syncRoot = new object(); private DoubleCheckLockSingleton() { } public static DoubleCheckLockSingleton GetInstance() { //先判斷執行個體是否存在,不存在再加鎖處理 if (instance == null) { lock (syncRoot) { if (instance == null) { instance = new DoubleCheckLockSingleton(); } } } return instance; } }
這樣即提高了效率,又解決了多線程下的單例問題。
在C#中提供了一種“靜態初始化”方法,這種方法不需要開發人員顯示的編寫安全執行緒代碼,即可解決多線程環境下的不安全問題,舉例如下:
/// <summary> /// 靜態初始化方法實現單例模式 /// 使用sealed關鍵字避免類的繼承,使用readonly使得變數只能在靜態初始化期間或類建構函式中分配變數 /// 私人的構造方法使得不能再類本身以外的地方執行個體化Singleton類 /// 這種方法中的執行個體是在類被載入時就執行個體化,因此被稱為餓漢模式 /// </summary> sealed class Singleton { //第一次引用類的任何成員時建立執行個體,公用語言運行庫負責處理變數的初始化 private static readonly Singleton instance = new Singleton(); private Singleton() { } public static Singleton GetInstance() { return instance; } }
由於餓漢式即靜態初始化方式是在類一載入就執行個體化對象,所以要提前佔用系統資源,而懶漢式又需要雙重鎖定機制保證安全執行緒,所以採用哪種方式,取決於實際需要。從C#語言角度來講,餓漢式的單例類已經能夠滿足我們的需求了。
單例模式