一般的,設(shè)計模式中用到單例模式,代碼通常會如下:
成都一家集口碑和實力的網(wǎng)站建設(shè)服務商,擁有專業(yè)的企業(yè)建站團隊和靠譜的建站技術(shù),10多年企業(yè)及個人網(wǎng)站建設(shè)經(jīng)驗 ,為成都1000+客戶提供網(wǎng)頁設(shè)計制作,網(wǎng)站開發(fā),企業(yè)網(wǎng)站制作建設(shè)等服務,包括成都營銷型網(wǎng)站建設(shè),品牌網(wǎng)站制作,同時也為不同行業(yè)的客戶提供做網(wǎng)站、成都網(wǎng)站建設(shè)的服務,包括成都電商型網(wǎng)站制作建設(shè),裝修行業(yè)網(wǎng)站制作建設(shè),傳統(tǒng)機械行業(yè)網(wǎng)站建設(shè),傳統(tǒng)農(nóng)業(yè)行業(yè)網(wǎng)站制作建設(shè)。在成都做網(wǎng)站,選網(wǎng)站制作建設(shè)服務商就選創(chuàng)新互聯(lián)建站。
public sealed class Singleton { private static Singleton instance=null; private Singleton() { } public static Singleton Instance { get { if (instance==null) { instance = new Singleton(); } return instance; } } }
代碼比較簡單,用到一個公有的靜態(tài)屬性和一個私有的靜態(tài)字段。并且把構(gòu)造函數(shù)設(shè)為私有,防止該類被實例化。
但上述代碼在多線程情況下并不可靠。有一種情況下。2個線程在get的時候,都檢測到instance==null,因此各自創(chuàng)建了一個Singleton對象,破壞了單例的原則。
因此改進后的代碼就是加鎖。
public sealed class Singleton { private static Singleton instance = null; private static readonly object padlock = new object(); Singleton() { } public static Singleton Instance { get { lock (padlock) { if (instance == null) { instance = new Singleton(); } return instance; } } } }
加鎖之后,多線程的操作也變的同步了,同一時間只能有獲得鎖的那個線程才能創(chuàng)建對象。這保證了對象的唯一,但是這個會損耗性能。
因為每次get的時候,都會加鎖,因此可以把代碼修改一下,如果對象已經(jīng)存在了,就不需要加鎖來創(chuàng)建對象。代碼修改如下:
public sealed class Singleton { private static volatile Singleton instance = null; private 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; } } }
仔細看一下,就會發(fā)現(xiàn),上述代碼中,有2次檢測instance == null,因此也成為雙重檢測。注意,由于Java中內(nèi)存模型的問題,上述雙重檢測代碼,對Java不一定有效。
在C#代碼中,考慮下面的場景,有2個線程A,B
1.A線程進入get
2.A線程檢測到instance==null
3.A線程獲得鎖
4.A線程實例化一個對象
5.B線程進入get
6.B線程檢測是否instance==null
問題出現(xiàn)在第4,5,6步驟。
當?shù)?步執(zhí)行的時候,很有可能出現(xiàn)這種情況,CLR為Singleton對象在托管堆上分配了空間,并且讓instance指向了這個空間,然后再去調(diào)用Singleton的構(gòu)造函數(shù)。而漏洞就在這里,線程B可能未等到線程A運行完Singleton的構(gòu)造函數(shù),就進入get檢測instance!=null,認為對象不是null,然后就直接返回instance,而此時,instance指向的值還在初始化呢,這就可能導致線程B得到的對象是沒完全初始化成功的,可能引起代碼錯誤。當然,這種錯誤的可能性非常少見,但還是會有一定的概率。
因此,上述代碼中instance變量加了一個關(guān)鍵字volatile,加它的作用,是為了訪問這個instance的時候,確保instance分配了空間并且初始化完成了(volatile確保該字段在任何時間呈現(xiàn)的都是最新的值)。
如果不使用volatile關(guān)鍵字,也可以將instance = new Singleton();語句替換成Interlocked.Exchange(ref instance,new Singleton())。
在C#中,對于實現(xiàn)單例模式,更為推崇的方法是使用靜態(tài)變量初始化。
有些設(shè)計模式的書中,避免使用靜態(tài)初始化的原因之一是C++ 規(guī)范在靜態(tài)變量的初始化順序方面留下了一些多義性。幸運的是,.NET Framework 通過其變量初始化處理方法解決了這種多義性:
public sealed class Singleton { private static readonly Singleton instance = new Singleton(); private Singleton(){} public static Singleton Instance { get { return instance; } } }
CLR保證了靜態(tài)字段初始化操作總是線程安全的,無論多少線程同時訪問該類,類中的靜態(tài)字段只可能被初始化一次(每個應用程序域)。
這是因為,類中的靜態(tài)字段的初始化,是由類的靜態(tài)構(gòu)造函數(shù)完成的,C#編譯器檢測到類中有靜態(tài)字段后,會為該類生產(chǎn)一個靜態(tài)構(gòu)造函數(shù)(可以從IL代碼中看到.cctor方法),也就是說下面代碼是等價的:
class SomeType{ Static int x = 5; }
等價于
class SomeType { Static int x; Static SomeType() { x = 5; } }
因此上述的instance只會初始化一次。保證了單例。這種方法使用了CLR的特性,對于其他語言并不保證,是.NET平臺上推崇的一種實現(xiàn)singleton的方式。
上述實現(xiàn)方法有個不足在于不能延遲加載對象,如果Singleton中還有其他靜態(tài)字段,引用該靜態(tài)字段的時候,會導致Singleton被創(chuàng)建了。因此,可以在該類中再嵌套一個類來實現(xiàn)延遲加載。如下:
public sealed class Singleton { private Singleton() { } public static Singleton Instance { get { return Nested.instance; } } private class Nested { internal static readonly Singleton instance = new Singleton(); } }
上述代碼,只有當訪問Instance屬性的時候,才會觸發(fā)Nested類的靜態(tài)字段,從而初始化一個Singleton對象,因此實現(xiàn)了延遲加載,但是設(shè)計比較復雜,不推薦使用。
在.NET4.0中,還有一種更優(yōu)雅的方法實現(xiàn)延遲加載,即使用Lazy<T>對象。
public sealed class Singleton { private static readonly Lazy<Singleton> lazy = new Lazy<Singleton>(() => new Singleton()); public static Singleton Instance { get { return lazy.Value; } } private Singleton() { } }
可以參考之前總結(jié)的文章http://cnn237111.blog.51cto.com/2359144/1213187
參考:
http://csharpindepth.com/Articles/General/Singleton.aspx#unsafe
http://msdn.microsoft.com/en-us/library/ff650316.aspx
http://mcwilling.blog.163.com/blog/static/1950971712013357359564/
NET4.0面向?qū)ο缶幊搪劵A(chǔ)篇.金旭亮
新聞名稱:C#中的單例模式
瀏覽地址:http://aaarwkj.com/article2/jeejic.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供品牌網(wǎng)站制作、ChatGPT、虛擬主機、網(wǎng)站內(nèi)鏈、網(wǎng)站收錄、外貿(mào)網(wǎng)站建設(shè)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)