C#設計模式——單例模式

可小巨集發表於2020-08-18

一、單例模式的介紹

  什麼是單例模式,官方定義的是:確保一個類只有一個例項,並提供一個全域性訪問點

二、為什麼會有單例模式

從單例模式的定義中我們可以看出,單例模式的使用自然是當我們的系統中某個物件只需要一個例項的情況,例如:作業系統中只能有一個工作管理員,操作檔案時,同一時間內只允許一個例項對其操作等。既然現實中有這樣的應用場景,所以也就有了單例模式了。

三、單例模式的實現思路

 1     /// <summary>
 2     /// 單例模式實現
 3     /// </summary>
 4     public class Singleton
 5     {
 6         //定義一個靜態變數儲存類的例項
 7         private static Singleton singleton;
 8 
 9         /// <summary>
10         /// 定義私有建構函式
11         /// </summary>
12         private Singleton()
13         {
14         }
15 
16         /// <summary>
17         /// 定義公有方法提供一個全域性訪問點,同時你也可以定義公有屬性來提供全域性訪問點
18         /// </summary>
19         /// <returns></returns>
20         public static Singleton GetSingleton()
21         {
22             // 如果類的例項不存在則建立,否則直接返回
23             if (singleton==null)
24             {
25                 singleton = new Singleton();
26             }
27             return singleton;
28         }
29     }

上面的單例模式在單執行緒的下是沒問題的,但是在多執行緒的情況下會得到多個Singleton例項,因為兩個執行緒在同時執行GetSingleton()方法時,此時兩個執行緒判斷(singleton==null)這個條件時返回都為真,此時兩個執行緒就建立了兩個Singleton例項,這樣就違背單例模式的初衷了,既然上面的實現會執行多個執行緒執行,那我們對於多執行緒的解決方案自然就是使GetSingleton()方法在同一時間只執行一個執行緒執行就好了,也就是我們執行緒同步的問題了,具體的解決多執行緒的程式碼如下:

 1     /// <summary>
 2     /// 單例模式實現
 3     /// </summary>
 4     public class Singleton
 5     {
 6         //定義一個靜態變數儲存類的例項
 7         private static Singleton singleton;
 8 
 9         // 定義一個標識確保執行緒同步
10         private static readonly object locker = new object();
11 
12         /// <summary>
13         /// 定義私有建構函式
14         /// </summary>
15         private Singleton()
16         {
17         }
18 
19         /// <summary>
20         /// 定義公有方法提供一個全域性訪問點,同時你也可以定義公有屬性來提供全域性訪問點
21         /// </summary>
22         /// <returns></returns>
23         public static Singleton GetSingleton()
24         {
25             // 當第一個執行緒執行到這裡時,此時會對locker物件 "加鎖",
26             // 當第二個執行緒執行該方法時,首先檢測到locker物件為"加鎖"狀態,該執行緒就會掛起等待第一個執行緒解鎖
27             // lock語句執行完之後(即執行緒執行完之後)會對該物件"解鎖"
28             lock (locker)
29             {
30                 // 如果類的例項不存在則建立,否則直接返回
31                 if (singleton == null)
32                 {
33                     singleton = new Singleton();
34                 }
35             }
36             return singleton;
37         }
38     }

上面這種解決方案確實可以解決多執行緒的問題,但是上面程式碼對於每個執行緒都會對執行緒輔助物件locker加鎖之後再判斷例項是否存在,對於這個操作完全沒有必要的,因為當第一個執行緒建立了該類的例項之後,後面的執行緒此時只需要直接判斷(singleton==null)為假,此時完全沒必要對執行緒輔助物件加鎖之後再去判斷,所以上面的實現方式增加了額外的開銷,損失了效能,為了改進上面實現方式的缺陷,我們只需要在lock語句前面加一句(singleton==null)的判斷就可以避免鎖所增加的額外開銷,這種實現方式我們就叫它 “雙重鎖定”,下面具體看看實現程式碼:

 1     /// <summary>
 2     /// 單例模式實現
 3     /// </summary>
 4     public class Singleton
 5     {
 6         //定義一個靜態變數儲存類的例項
 7         private static Singleton singleton;
 8 
 9         // 定義一個標識確保執行緒同步
10         private static readonly object locker = new object();
11 
12         /// <summary>
13         /// 定義私有建構函式
14         /// </summary>
15         private Singleton()
16         {
17         }
18 
19         /// <summary>
20         /// 定義公有方法提供一個全域性訪問點,同時你也可以定義公有屬性來提供全域性訪問點
21         /// </summary>
22         /// <returns></returns>
23         public static Singleton GetSingleton()
24         {
25             // 當第一個執行緒執行到這裡時,此時會對locker物件 "加鎖",
26             // 當第二個執行緒執行該方法時,首先檢測到locker物件為"加鎖"狀態,該執行緒就會掛起等待第一個執行緒解鎖
27             // lock語句執行完之後(即執行緒執行完之後)會對該物件"解鎖"
28             if (singleton == null)
29             {
30                 lock (locker)
31                 {
32                     // 如果類的例項不存在則建立,否則直接返回
33                     if (singleton == null)
34                     {
35                         singleton = new Singleton();
36                     }
37                 }
38             }
39             return singleton;
40         }
41     }

四、總結

到這裡,設計模式的單例模式就介紹完了,希望通過本文章大家可以對單例模式有一個更深的理解。

相關文章