第一種最簡單,但沒有考慮線程安全,在多線程時可能會出問題,不過俺從沒看過出錯的現象,表鄙視我…… public class Singleton{ private static Singleton _instance = null; private Singleton(){} public stati ...
第一種最簡單,但沒有考慮線程安全,在多線程時可能會出問題,不過俺從沒看過出錯的現象,表鄙視我……
public class Singleton
{
private static Singleton _instance = null;
private Singleton(){}
public static Singleton CreateInstance()
{
if(_instance == null)
{
_instance = new Singleton();
}
return _instance;
}
}
第二種考慮了線程安全,不過有點煩,但絕對是正規寫法,經典的一叉
public class Singleton
{
private volatile static Singleton _instance = null;
private static readonly object lockHelper = new object();
private Singleton(){}
public static Singleton CreateInstance()
{
if(_instance == null)
{
lock(lockHelper)
{
if(_instance == null)
_instance = new Singleton();
}
}
return _instance;
}
}
第三種可能是C#這樣的高級語言特有的,實在懶得出奇
public class Singleton
{
private Singleton(){}
public static readonly Singleton instance = new Singleton();
}
一、 單例(Singleton)模式
單例模式的特點:
- 單例類只能有一個實例。
- 單例類必須自己創建自己的唯一實例。
- 單例類必須給所有其它對象提供這一實例。
單例模式應用:
- 每台電腦可以有若幹個印表機,但只能有一個Printer Spooler,避免兩個列印作業同時輸出到印表機。
- 一個具有自動編號主鍵的表可以有多個用戶同時使用,但資料庫中只能有一個地方分配下一個主鍵編號。否則會出現主鍵重覆。
二、 Singleton模式的結構:
Singleton模式包含的角色只有一個,就是Singleton。Singleton擁有一個私有構造函數,確保用戶無法通過new直接實例它。除此之外,該模式中包含一個靜態私有成員變數instance與靜態公有方法Instance()。Instance方法負責檢驗並實例化自己,然後存儲在靜態成員變數中,以確保只有一個實例被創建。(關於線程問題以及C#所特有的Singleton將在後面詳細論述)。
三、 程式舉例:
該程式演示了Singleton的結構,本身不具有任何實際價值。
// Singleton pattern -- Structural exampleusing System;
// "Singleton"
class Singleton
{
// Fields
private static Singleton instance;
// Constructor
protected Singleton() {}
// Methods
public static Singleton Instance()
{
// Uses "Lazy initialization"
if( instance == null )
instance = new Singleton();
return instance;
}
}
/// <summary>
/// Client test
/// </summary>
public class Client
{
public static void Main()
{
// Constructor is protected -- cannot use new
Singleton s1 = Singleton.Instance();
Singleton s2 = Singleton.Instance();
if( s1 == s2 )
Console.WriteLine( "The same instance" );
}
}
四、 在什麼情形下使用單例模式:
使用Singleton模式有一個必要條件:在一個系統要求一個類只有一個實例時才應當使用單例模式。反過來,如果一個類可以有幾個實例共存,就不要使用單例模式。
註意:
不要使用單例模式存取全局變數。這違背了單例模式的用意,最好放到對應類的靜態成員中。
不要將資料庫連接做成單例,因為一個系統可能會與資料庫有多個連接,並且在有連接池的情況下,應當儘可能及時釋放連接。Singleton模式由於使用靜態成員存儲類實例,所以可能會造成資源無法及時釋放,帶來問題。
五、 Singleton模式在實際系統中的實現
下麵這段Singleton代碼演示了負載均衡對象。在負載均衡模型中,有多台伺服器可提供服務,任務分配器隨機挑選一臺伺服器提供服務,以確保任務均衡(實際情況比這個複雜的多)。這裡,任務分配實例只能有一個,負責挑選伺服器並分配任務。
// Singleton pattern -- Real World exampleusing System;
using System.Collections;
using System.Threading;
// "Singleton"
class LoadBalancer
{
// Fields
private static LoadBalancer balancer;
private ArrayList servers = new ArrayList();
private Random random = new Random();
// Constructors (protected)
protected LoadBalancer()
{
// List of available servers
servers.Add( "ServerI" );
servers.Add( "ServerII" );
servers.Add( "ServerIII" );
servers.Add( "ServerIV" );
servers.Add( "ServerV" );
}
// Methods
public static LoadBalancer GetLoadBalancer()
{
// Support multithreaded applications through
// "Double checked locking" pattern which avoids
// locking every time the method is invoked
if( balancer == null )
{
// Only one thread can obtain a mutex
Mutex mutex = new Mutex();
mutex.WaitOne();
if( balancer == null )
balancer = new LoadBalancer();
mutex.Close();
}
return balancer;
}
// Properties
public string Server
{
get
{
// Simple, but effective random load balancer
int r = random.Next( servers.Count );
return servers[ r ].ToString();
}
}
}
/// <summary>
/// SingletonApp test
/// </summary>
///
public class SingletonApp
{
public static void Main( string[] args )
{
LoadBalancer b1 = LoadBalancer.GetLoadBalancer();
LoadBalancer b2 = LoadBalancer.GetLoadBalancer();
LoadBalancer b3 = LoadBalancer.GetLoadBalancer();
LoadBalancer b4 = LoadBalancer.GetLoadBalancer();
// Same instance?
if( (b1 == b2) && (b2 == b3) && (b3 == b4) )
Console.WriteLine( "Same instance" );
// Do the load balancing
Console.WriteLine( b1.Server );
Console.WriteLine( b2.Server );
Console.WriteLine( b3.Server );
Console.WriteLine( b4.Server );
}
}
六、 C#中的Singleton模式
C#的獨特語言特性決定了C#擁有實現Singleton模式的獨特方法。這裡不再贅述原因,給出幾個結果:
方法一:
下麵是利用.NET Framework平臺優勢實現Singleton模式的代碼:
sealed class Singleton{
private Singleton();
public static readonly Singleton Instance=new Singleton();
}
這使得代碼減少了許多,同時也解決了線程問題帶來的性能上損失。那麼它又是怎樣工作的呢?
註意到,Singleton類被聲明為sealed,以此保證它自己不會被繼承,其次沒有了Instance的方法,將原來_instance成員變數變成public readonly,併在聲明時被初始化。通過這些改變,我們確實得到了Singleton的模式,原因是在JIT的處理過程中,如果類中的static屬性被任何方法使用時,.NET Framework將對這個屬性進行初始化,於是在初始化Instance屬性的同時Singleton類實例得以創建和裝載。而私有的構造函數和readonly(只讀)保證了Singleton不會被再次實例化,這正是Singleton設計模式的意圖。
(摘自:http://www.cnblogs.com/huqingyu/archive/2004/07/09/22721.aspx )
不過這也帶來了一些問題,比如無法繼承,實例在程式一運行就被初始化,無法實現延遲初始化等。
詳細情況可以參考微軟MSDN文章:《Exploring the Singleton Design Pattern》
方法二:
既然方法一存在問題,我們還有其它辦法。
public sealed class Singleton
{
Singleton()
{
}
public static Singleton GetInstance()
{
return Nested.instance;
}
class Nested
{
// Explicit static constructor to tell C# compiler
// not to mark type as beforefieldinit
static Nested()
{
}
internal static readonly Singleton instance = new Singleton();
}
}
這實現了延遲初始化,並具有很多優勢,當然也存在一些缺點。詳細內容請訪問:《Implementing the Singleton Pattern in C#》。文章包含五種Singleton實現,就模式、線程、效率、延遲初始化等很多方面進行了詳細論述。
參考鏈接:http://blog.csdn.net/zhuangzhineng/article/details/3927455
單 例模式是廣為流傳的設計模式中的一種。本質上,單例模式是一個只允許創建一個實例,並提供對這個實例簡單的訪問途徑的類。一般而言,單例模式在創建實例時 不允許傳遞任何參數-否則不同參數導致不同的實例創建,就會出現問題!(如果同一個實例可以被同參的不同請求所訪問,那麼工廠模式會更適合。)這篇文章只 針對無參創建的請求進行討論。典型的,單例模式的應用往往是延後創建的(created lazily)---只有在第一次被用到的時候才會被創建。在C#中有實現單例模式有很多種方法。我將在這裡一一展現給大家,從最常見的、線程不安全的到 延後創建的、線程安全的、再到簡潔高效的版本。註意在下麵的代碼中,我忽略了所有私有域,因為私有域是預設的類的成員。In many other languages such as Java, there is a different default, and private should be used.
所有的這些實現都有以下四個特征:
1.只有一個構造函數,而且是私有的,不帶參數的。這是為了防止其他類對其實例化(這和模式本身有衝突)。同時也防止了子類化--如果一個單例能被子類化 一次,就能被子類化兩次,而如果每個子類可以創建一個實例,這與模式本身又產生了衝突。如果你(遇到這樣的情況):只有在運行期才能知道實際的類型,因此 你需要一個父類的單例,你可以使用工廠模式。
2.類是密封的。這並不是必須的,嚴格的說,即如上一點所說的原因,可以提高JIT(Just-In-Time , 運行時編譯執行的技術)的效率。
3.一個靜態變數用來保存單例的引用。
4.一個用以訪問單例引用的公用靜態方法。
註意:所有這些實現都用到一個公用靜態屬性Instance,作為訪問實例的方法。當然都可以替換為方法,這對線程安全和性能都沒有影響。
版本1-非線程安全
// Bad code! Do not use!
public sealed class Singleton
{
static Singleton instance=null;
Singleton()
{
}
public static Singleton Instance
{
get
{
if (instance==null)
{
instance = new Singleton();
}
return instance;
}
}
}
如 上提示,上面的代碼是非線程安全的。兩個線程可能同時判斷“if (instance==null)”,發現為TRUE,於是都創建了實例,這又違背了單例模式。註意:事實上在表達式反應前實例已經被創建了,記憶體模型並 不保證新的實例的值被其他的線程看到,除非對應的記憶體屏障已經通過了。(CPU越過記憶體屏障後,將刷新自已對存儲器的緩衝狀態,這樣其他線程才能同步自己 的copy)
版本2 - 簡單的線程安全
public sealed class Singleton
{
static Singleton instance=null;
static readonly object padlock = new object();
Singleton()
{
}
public static Singleton Instance
{
get
{
lock (padlock)
{
if (instance==null)
{
instance = new Singleton();
}
return instance;
}
}
}
}
這 個實現是線程安全的。線程首先對共用的對象進行鎖定,然後判斷實例是否在之前已經創建。這裡要小心記憶體屏障問題(在鎖定的時候確保所有的讀操作發生在所獲 得之後,在解鎖的時候確保所有的寫操作發生在鎖釋放之前),確保只有一個線程創建了實例(因為在同一時刻只有一個線程可以在執行那段代碼--到了第二個線 程進入的時候,第一個線程已經完成了實例的創建,這樣表達式返回false)。不幸的是,由於在每次訪問的時候都要進行鎖定,所以影響了性能。(這對於多 線程併發的高性能要求的應用顯得尤為重要)。註意有些該版本的實現對typeof(Singleton)進行鎖定,但我是對類的私有靜態變數進行鎖定。對 那些可被其他類訪問的對象進行鎖定或對類型進行鎖定會導致性能問題甚至引起死鎖。這是一個地雷-任何地方都有可能發生,只有對本就為鎖定而創建的對象進行 鎖定或是那些因為某些目的而被鎖定的文檔才是安全的。通常這些對象都是私有的。這樣有助於寫出線程安全的程式。
版本3 -- 用雙重檢測機制的線程安全
// Bad code! Do not use!
public sealed class Singleton
{
static Singleton instance=null;
static readonly object padlock = new object();
Singleton()
{
}
public static Singleton Instance
{
get
{
if (instance==null)
{
lock (padlock)
{
if (instance==null)
{
instance = new Singleton();
}
}
}
return instance;
}
}
}
C#單例模式的幾種實現方式
文章總結自張波老師的視頻教程
單例模式
-
動機(Motivation)
- 在軟體系統中,經常有這樣一些特殊的類,必須保證它們在系統中只存在一個實例,才能確保它們的邏輯正確性、以及良好的效率。
- 如何繞過常規的構造器,提供一種機制來保證一個類只有一個實例?
- 這應該是類設計者的責任,而不是使用者的責任
-
意圖(Intent)
- 保證一個類僅有一個實例,並提供一個該實例的全局訪問點。——《設計模式》GoF
簡單實現
public sealed class Singleton
{
private static Singleton _instance = null;
private Singleton()
{
}
public static Singleton Instance
{
get { return _instance ?? (_instance = new Singleton()); }
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
說明:
- 對於線程來說不安全
- 單線程中已滿足要求
- 優點:
- 由於實例是在 Instance 屬性方法內部創建的,因此類可以使用附加功能
- 直到對象要求產生一個實例才執行實例化;這種方法稱為“惰性實例化”。惰性實例化避免了在應用程式啟動時實例化不必要的 singleton。
線程安全的
public sealed class Singleton
{
private static Singleton _instance = null;
private static readonly object Padlock = new object();
private Singleton()
{
}
public static Singleton Instance
{
get
{
lock (Padlock)
{
if (_instance == null)
{
_instance = new Singleton();
}
}
return _instance;
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
說明:
- 同一個時刻加了鎖的那部分程式只有一個線程可以進入
- 對象實例由最先進入的那個線程創建
- 後來的線程在進入時(instence == null)為假,不會再去創建對象實例
- 增加了額外的開銷,損失了性能
雙重鎖定
public sealed class Singleton
{
private static Singleton _instance = null;
private static readonly object Padlock = new object();
private Singleton()
{
}
public static Singleton Instance
{
get
{
if (_instance == null)
{
lock (Padlock)
{
if (_instance == null)
{
_instance = new Singleton();
}
}
}
return _instance;
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
說明:
- 多線程安全
- 線程不是每次都加鎖
- 允許實例化延遲到第一次訪問對象時發生
靜態初始化
public sealed class Singleton
{
private static readonly Singleton _instance = null;
static Singleton()
{
_instance = new Singleton();
}
private Singleton()
{
}
public static Singleton Instance
{
get
{
return _instance;
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
說明:
- 依賴公共語言運行庫負責處理變數初始化
- 公共靜態屬性為訪問實例提供了一個全局訪問點
- 對實例化機制的控制權較少(.NET代為實現)
- 靜態初始化是在 .NET 中實現 Singleton 的首選方法
延遲初始化
public sealed class Singleton
{
private Singleton()
{
}
public static Singleton Instance
{
get
{
return Nested.instance;
}
}
private class Nested
{
internal static readonly Singleton instance = null;
static Nested()
{
instance = new Singleton();
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
說明:
- 初始化工作由Nested類的一個靜態成員來完成,這樣就實現了延遲初始化
註意事項
- Singleton模式中的實例構造器可以設置為protected以允許子類派生。
- Singleton模式一般不要支持ICloneable介面,因為這可能會導致多個對象實例,與Singleton模式的初衷違背。
- Singleton模式一般不要支持序列化,因為這也有可能導致多個對象實例,同樣與Singleton模式的初衷違背。
- Singletom模式只考慮到了對象創建的管理,沒有考慮對象銷毀的管理。就支持垃圾回收的平臺和對象的開銷來講,我們一般沒有必要對其銷 毀進行特殊的管理。
總結
- Singleton模式是限制而不是改進類的創建。
- 理解和擴展Singleton模式的核心是“如何控制用戶使用new對一個類的構造器的任意調用”。
- 可以很簡單的修改一個Singleton,使它有少數幾個實例,這樣做是允許的而且是有意義的。