1. 程式人生 > >三種方式構建C#單例模式

三種方式構建C#單例模式

同時 編譯 lec moni 單例對象 spa code exc 另一個

  1     /// <summary>
  2     /// 雙檢鎖實現單例
  3     /// </summary>
  4     public sealed class SingletonDoubleCheck
  5     {
  6         //s_lock對象是實現線程安全所需要的,定義這個對象時,我們假設創建單例對象的代價高於創建一個System.Object對象
  7         //並假設可能根本不需要創建單例對象,否則,更經濟、更簡單的做法是在一個類構造器中創建單例對象
  8         private static Object s_lock = new
Object(); 9 10 //這個字段引用一個單例對象 11 private static SingletonDoubleCheck s_value = null; 12 13 //私有構造器阻止這個類外部的任何代碼創建實例 14 private SingletonDoubleCheck() 15 { 16 17 } 18 19 //以下公共靜態方法返回單例對象(如有必要就創建它) 20 public static SingletonDoubleCheck GetSingleton()
21 { 22 //如果單例對象已經創建,則直接返回它 23 if (s_value != null) 24 { 25 return s_value; 26 } 27 28 //在指定對象上獲取排他鎖 29 Monitor.Enter(s_lock); 30 31 //再次檢查是否已經創建 32 //解決問題:若多個線程首次(單例對象還未創建)同時進入,此時,只有一個線程執行下面的代碼(因為有Monitor),
33 //當該線程(第一個線程)創建完實例後,另一個線程(第二個線程)立即獲得鎖,也執行下面的代碼, 34 //此時實例已經創建完成,後面的線程應該直接返回才對,因此再判斷一次實例是否已經創建。 35 if (s_value == null) 36 { 37 //若仍未創建則創建它 38 SingletonDoubleCheck singleton = new SingletonDoubleCheck(); 39 40 //將singleton給s_value 41 //下面的代碼保證singleton中的引用只有在構造器結束執行之後才發布到s_value中 42 Volatile.Write(ref s_value, singleton); 43 44 //註意:下面的寫法是不牢靠的 45 //因為編譯器可能會這樣做: 46 //1.為SingletonDoubleCheck分配內存 47 //2.將引用發布到(賦給)s_value 48 //3.調用構造器 49 //假設在將引用發布給s_value之後,但在調用構造器之前,若有另一個線程調用了GetSingleton, 50 //此時s_value不為null,該線程會使用該對象,但該對象的構造器還沒執行完成。 51 //s_value = new SingletonDoubleCheck(); 52 } 53 54 //釋放指定對象上的排他鎖 55 Monitor.Exit(s_lock); 56 57 return s_value; 58 } 59 } 60 61 /// <summary> 62 /// C#下簡單的構造單例方法 63 /// CLR已保證了對類的構造是線程安全的,書寫非常簡便 64 /// 缺點也很明顯,首次訪問類的任何成員時都會調用類型構造器 65 /// 所以,如果該類定義了其它靜態成員,就會在訪問其它任何靜態成員時創建該對象 66 /// </summary> 67 public sealed class SingletonSimple 68 { 69 private static SingletonSimple s_value = new SingletonSimple(); 70 71 //私有構造器阻止這個類外部的任何代碼創建實例 72 private SingletonSimple() 73 { 74 75 } 76 77 //以下公共靜態方法返回單例對象(如有必要就創建它) 78 public static SingletonSimple GetSingleton() 79 { 80 return s_value; 81 } 82 83 // 84 public static SingletonSimple SingletonInstance 85 { 86 get { return s_value; } 87 } 88 89 // 90 public static SingletonSimple Instance { get; } = new SingletonSimple(); 91 } 92 93 /// <summary> 94 /// 嵌套類實現單例 95 /// 如果多個線程同時調用GetSingleton,則可能創建多個SingletonNested對象 96 /// 但由於使用了Interlocked.CompareExchange,所以保證只會有一個引用被發布到s_value 97 /// 沒有被固定下來的對象都會被垃圾回收 98 /// 該種方式不會阻塞線程 99 /// </summary> 100 public sealed class SingletonNested 101 { 102 private static SingletonNested s_value = null; 103 104 //私有構造器阻止這個類外部的任何代碼創建實例 105 private SingletonNested() 106 { 107 108 } 109 110 //以下公共靜態方法返回單例對象(如有必要就創建它) 111 public static SingletonNested GetSingleton() 112 { 113 //如果單例對象已經創建,則直接返回它 114 if (s_value != null) 115 { 116 return s_value; 117 } 118 119 //創建一個新的單例對象,並把它固定下來(如果另一個線程還沒有固定它的話) 120 SingletonNested singletonNested = new SingletonNested(); 121 122 //比較兩個指定的引用類型的實例 T 是否相等,如果相等,則替換第一個,並且返回s_value原始值 123 //s_value與null比較,如果相等則用singletonNested替換s_value,否則不替換 124 Interlocked.CompareExchange(ref s_value, singletonNested, null); 125 126 //如果該線程競爭失敗,則新建的第二個單實例對象會被垃圾回收 127 128 return s_value; 129 } 130 }

三種方式構建C#單例模式