一個類只容許建立惟一一個對象(或者實例),那這個類就是一個單例類,這種設計模式就叫做單例設計模式,簡稱單例模式。git
單例模式多是你們據說最多的設計模式了,網上介紹最多的設計模式大概就是單例模式了,我看過的設計模式相關的文章不少都是寫一篇介紹單例模式,而後就沒有了。程序員
經典的設計模式有 23 種, 若是隨便抓一個程序員,讓他說一說最熟悉的 3 種設計模式,那其中確定會包含今天要講的單例模式,github
單例模式主要用來確保某個類型的實例只能有一個。好比手機上的藍牙之類的只能有一個的實例的場景能夠考慮用單例模式。設計模式
主要做用:安全
單例模式的實現,一般須要私有化構造方法,防止外部類直接使用單例類的構造方法建立對象多線程
public class Singleton { private static Singleton _instance; private Singleton() { } public static Singleton GetInstance() { if (_instance == null) { _instance = new Singleton(); } return _instance; } }
這種方式比較簡單,可是不是線程安全的,多線程高併發狀況下可能會致使建立多個實例,可是若是你的業務場景容許建立多個,我以爲問題也不大,若是必定要保證只能建立一個實例,能夠參考下面的作法架構
/// <summary> /// 雙重判空加鎖,飽漢模式(懶漢式),用到的時候再去實例化 /// </summary> public class Singleton { private static volatile Singleton _instance; private static readonly object SyncLock = new object(); private Singleton() { } public static Singleton GetInstance() { if (_instance == null) { lock (SyncLock) { if (_instance == null) { _instance = new Singleton(); } } } return _instance; } }
這種方式的執行過程會先檢查是否完成了實例化,若是已經實例化則直接返回實例,若是沒有就嘗試獲取鎖,得到鎖以後再判斷一下是否已經實例化,若是已經實例化則返回實例,若是沒有就進行實例化併發
/// <summary> /// 餓漢模式-就是屌絲,擔憂餓死。類加載就給準備好 /// </summary> public sealed class Singleton1 { /// <summary> /// 靜態初始化,由 CLR 去建立,無需加鎖 /// </summary> private static readonly Singleton1 Instance = new Singleton1(); private Singleton1() { } public static Singleton1 GetInstance() => Instance; }
這也是一種常見的實現單例模式的用法,可是這種方式就不支持懶加載了,不像上面那種方式能夠作到須要的時候再實例化,適用於這個對象會被頻繁使用或者這個類比較小,是否實例化沒有什麼影響。框架
這個是以前忘記在哪裏看到的微軟框架裏的一段代碼,相似,可能和源碼並不徹底同樣,只是提供一種實現思路asp.net
/// <summary> /// 使用 ConcurrentDictionary 實現的單例方法,用到的時候再去實例化 /// 這種方式相似於第一種方式,只是使用了併發集合代替了雙重判斷和 lock /// </summary> public class Singleton2 { private static readonly ConcurrentDictionary<int, Singleton2> Instances = new ConcurrentDictionary<int, Singleton2>(); private Singleton2() { } public static Singleton2 GetInstance() => Instances.GetOrAdd(1, k => new Singleton2()); }
C# 裏提供了 Lazy
的方式實現延遲實例化
/// <summary> /// 使用 Lazy 實現的單例方法,用到的時候再去實例化 /// </summary> public class Singleton3 { private static readonly Lazy<Singleton3> LazyInstance = new Lazy<Singleton3> (() => new Singleton3()); private Singleton3() { } public static Singleton3 GetInstance() => LazyInstance.Value; }
你也可使用內部類等實現方式,這裏就不介紹了,想了解能夠本身網上找一下
驗證是否線程安全,驗證示例代碼:
Console.WriteLine($"Singleton"); Enumerable.Range(1, 10).Select(i => Task.Run(() => { Console.WriteLine($"{Singleton.GetInstance().GetHashCode()}"); })).WhenAll().Wait(); Console.WriteLine($"Singleton1"); Enumerable.Range(1, 10).Select(i => Task.Run(() => { Console.WriteLine($"{Singleton1.GetInstance().GetHashCode()}"); })).WhenAll().Wait(); Console.WriteLine($"Singleton2"); Enumerable.Range(1, 10).Select(i => Task.Run(() => { Console.WriteLine($"{Singleton2.GetInstance().GetHashCode()}"); })).WhenAll().Wait(); Console.WriteLine($"Singleton3"); Enumerable.Range(1, 10).Select(i => Task.Run(() => { Console.WriteLine($"{Singleton3.GetInstance().GetHashCode()}"); })).WhenAll().Wait();
上面的 WhenAll
是一個擴展方法,就是調用的 Task.WhenAll
,輸出示例:
隨着如今依賴注入思想的普及,asp.net core 更是基於依賴框架構建的,使用依賴注入的方式能夠較好的解決上面的各類問題
基於依賴注入框架,你能夠沒必要擔憂對象的建立和銷燬,讓依賴注入框架管理對象,這樣這個要實現單例模式的類型能夠和其餘普通類型同樣,只須要使用依賴注入框架註冊服務的時候指定服務生命週期爲單例便可,好比使用微軟的依賴注入框架的時候可使用 services.AddSingleton<TSingletonService>();
來註冊單例服務