细颗粒度Singleton模式实现
作為一個(gè)很典型的設(shè)計(jì)模式,Singleton模式常常被用來展示設(shè)計(jì)模式的技巧,并且隨著技術(shù)的演進(jìn),.NET語言和Java都已經(jīng)把經(jīng)典《Design Patterns : Elements of Reusable Object-Oriented Software》中所定義的Singleton模式作了完善,例如C#可以通過這樣一個(gè)非常精簡(jiǎn)但又很完美的方式實(shí)現(xiàn)了一個(gè)進(jìn)程內(nèi)部線程安全的Singleton模式。
C#?最經(jīng)典Singleton模式的實(shí)現(xiàn)(Lazy構(gòu)造方式)public?class?Singleton{
????private?static?Singleton?instance;???//?唯一實(shí)例
????protected?Singleton()?{?}???//?封閉客戶程序的直接實(shí)例化
????public?static?Singleton?Instance????
????{
????????get
????????{
????????????if?(instance?==?null)
????????????????instance?=?new?Singleton();
????????????return?instance;
????????}
????}
}?
C# 通過Double Check實(shí)現(xiàn)的相對(duì)線程安全的Singleton模式?
public?class?Singleton{
????protected?Singleton()?{?}
????private?static?volatile?Singleton?instance?=?null;
????///?Lazy方式創(chuàng)建唯一實(shí)例的過程
????public?static?Singleton?Instance()
????{
????????if?(instance?==?null)???????????//?外層if
????????????lock?(typeof(Singleton))????//?多線程中共享資源同步
????????????????if?(instance?==?null)???//?內(nèi)層if
????????????????????instance?=?new?Singleton();
????????return?instance;
????}
}?
C#充分依靠語言特性實(shí)現(xiàn)的間接版Singleton模式
{
????private?Singleton()?{?}
????public?static?readonly?Singleton?Instance?=?new?Singleton();
}?
但項(xiàng)目中我們往往需要更粗或者更細(xì)顆粒度的Singleton,比如某個(gè)線程是長時(shí)間運(yùn)行的后臺(tái)任務(wù),它本身存在很多模塊和中間處理,但每個(gè)線程都希望有自己的線程內(nèi)單獨(dú)Singleton對(duì)象,其他線程也獨(dú)立操作自己的線程內(nèi)Singleton,所謂的線程級(jí)Singleton其實(shí)他的實(shí)例總數(shù) = 1(每個(gè)線程內(nèi)部唯一的一個(gè)) * N (線程數(shù))= N。
.NET程序可以通過把靜態(tài)成員標(biāo)示為System. ThreadStaticAttribute就可以確保它指示靜態(tài)字段的值對(duì)于每個(gè)線程都是唯一的。但這對(duì)于Windows Form程序很有效,對(duì)于Web Form、ASP.NET Web Service等Web類應(yīng)用不適用,因?yàn)樗麄兪窃谕粋€(gè)IIS線程下分割的執(zhí)行區(qū)域,客戶端調(diào)用時(shí)傳遞的對(duì)象是在HttpContext中共享的,也就是說它本身不可以簡(jiǎn)單地通過System. ThreadStaticAttribute實(shí)現(xiàn)。不僅如此,使用System. ThreadStaticAttribute也不能很瀟灑的套用前面的內(nèi)容寫成:
C#[ThreadStatic]
public?static?readonly?Singleton?Instance?=?new?Singleton();
因?yàn)榘凑?NET的設(shè)計(jì)要求不要為標(biāo)記為它的字段指定初始值,因?yàn)檫@樣的初始化只會(huì)發(fā)生一次,因此在類構(gòu)造函數(shù)執(zhí)行時(shí)只會(huì)影響一個(gè)線程。在不指定初始值的情況下,如果它是值類型,可依賴初始化為其默認(rèn)值的字段,如果它是引用類型,則可依賴初始化為null。也就是說多線程情況下,除了第一個(gè)實(shí)例外,其他線程雖然也期望通過這個(gè)方式獲得唯一實(shí)例,但其實(shí)獲得就是一個(gè)null,不能用。
解決Windows Form下的細(xì)顆粒度Singleton問題
對(duì)于Windows Forms下的情況,可以通過System. ThreadStaticAttribute比較容易的高速CLR其中的靜態(tài)唯一屬性Instance僅在本線程內(nèi)部靜態(tài),但麻煩的是怎么構(gòu)造它,正如上面背景介紹部分所說,不能把它放到整個(gè)類的靜態(tài)構(gòu)造函數(shù)里,也不能直接初始化,那么怎么辦?還好,那個(gè)很cool的實(shí)現(xiàn)這里不適用的話,我們就退回到最經(jīng)典的那個(gè)lazy方式加載Singleton實(shí)例的方法。你可能覺得,這線程不安全了吧?那種實(shí)現(xiàn)方式確實(shí)不是線程安全,但我們這里的Singleton構(gòu)造本身就已經(jīng)運(yùn)行在一個(gè)線程里面了,用那種不安全的方式在線程內(nèi)部實(shí)現(xiàn)只有自己“一畝三分地”范圍內(nèi)Singleton的對(duì)象反而安全了。新的實(shí)現(xiàn)如下:
public?class?Singleton{
????private?Singleton()?{?}
????[ThreadStatic]??//?說明每個(gè)Instance僅在當(dāng)前線程內(nèi)靜態(tài)
????private?static?Singleton?instance;
????public?static?Singleton?Instance
????{
????????get
????????{
????????????if?(instance?==?null)
????????????????instance?=?new?Singleton();
????????????return?instance;
????????}
????}
}?
Unit Test///?每個(gè)線程需要執(zhí)行的目標(biāo)對(duì)象定義///?同時(shí)在它內(nèi)部完成線程內(nèi)部是否Singleton的情況
class?Work
{
????public?static?IList?Log?=?new?List();
????///?每個(gè)線程的執(zhí)行部分定義
????public?void?Procedure()
????{
????????Singleton?s1?=?Singleton.Instance;
????????Singleton?s2?=?Singleton.Instance;
????????//?證明可以正常構(gòu)造實(shí)例
????????Assert.IsNotNull(s1);
????????Assert.IsNotNull(s2);
????????//?驗(yàn)證當(dāng)前線程執(zhí)行體內(nèi)部?jī)纱我玫氖欠駷橥粋€(gè)實(shí)例
????????Assert.AreEqual(s1.GetHashCode(),?s2.GetHashCode());
????????//登記當(dāng)前線程所使用的Singleton對(duì)象標(biāo)識(shí)
????????Log.Add(s1.GetHashCode());
????}
}
[TestClass]
public?class?TestSingleton
{
????private?const?int?ThreadCount?=?3;
????[TestMethod]
????public?void?Test()
????{
????????//?創(chuàng)建一定數(shù)量的線程執(zhí)行體
????????Thread[]?threads?=?new?Thread[ThreadCount];
????????for?(int?i?=?0;?i?<?ThreadCount;?i++)
????????{
????????????ThreadStart?work?=?new?ThreadStart((new?Work()).Procedure);
????????????threads[i]?=?new?Thread(work);
????????}
????????//?執(zhí)行線程
????????foreach?(Thread?thread?in?threads)?thread.Start();
????????//?終止線程并作其他清理工作
????????//?...?...
????????//?判斷是否不同線程內(nèi)部的Singleton實(shí)例是不同的
????????for?(int?i?=?0;?i?<?ThreadCount?-?1;?i++)
????????????for?(int?j?=?i?+?1;?j?<?ThreadCount;?j++)
????????????????Assert.AreNotEqual(Work.Log[i],?Work.Log[j]);
????}
}
下面我們分析一下單元測(cè)試代碼說明的問題:
- 在Work.Procedure()方法中,兩次調(diào)用到了Singleton類的Instance靜態(tài)屬性,經(jīng)過驗(yàn)證是同一個(gè)Singleton類實(shí)例。同時(shí)由于Singleton類的構(gòu)造函數(shù)定義為私有,所以線程(客戶程序)無法自己實(shí)例化Singleton類,因此同時(shí)滿足該模式的設(shè)計(jì)意圖;
- 通過對(duì)每個(gè)線程內(nèi)部使用的Singleton實(shí)例登記并檢查,確認(rèn)不同線程內(nèi)部其實(shí)掌握的是不同實(shí)例的引用,因此滿足我們需要實(shí)現(xiàn)的細(xì)顆粒度(線程級(jí))的意圖;
- 解決Web Form下細(xì)顆粒度Singleton問題。
上面用ThreadStatic雖然解決了Windows Form的問題,但對(duì)于Web Form應(yīng)用而言并不適用,原因是Web Form應(yīng)用中每個(gè)會(huì)話的本地全局區(qū)域不是線程,而是自己的HttpContext,因此相應(yīng)的Singleton實(shí)例也應(yīng)該保存在這個(gè)位置。實(shí)現(xiàn)上我們只需要做少許的修改,就可以完成一個(gè)Web Form下的細(xì)顆粒度Singleton設(shè)計(jì):
注:這里的Web Form應(yīng)用包括ASP.NET Application、ASP.NET Web Service、ASP.NET AJAX等相關(guān)應(yīng)用。但示例并沒有在.NET Compact Framework和.NET Micro Framework的環(huán)境下進(jìn)行過驗(yàn)證。
public?class?Singleton{
????///?足夠復(fù)雜的一個(gè)key值,用于和HttpContext中的其他內(nèi)容相區(qū)別
????private?const?string?Key?=?"just.complicated..singleton";
????private?Singleton()?{?}
????public?static?Singleton?Instance
????{
????????get
????????{
????????????//?基于HttpContext的Lazy實(shí)例化過程
????????????Singleton?instance?=?(Singleton)HttpContext.Current.Items[Key];
????????????if?(instance?==?null)
????????????{
????????????????instance?=?new?Singleton();
????????????????HttpContext.Current.Items[Key]?=?instance;
????????????}
????????????return?instance;
????????}
????}
}?
//Unit?Test
using?System;
using?System.Web;
using?MarvellousWorks.PracticalPattern.SingletonPattern.WebContext;
using?Microsoft.VisualStudio.TestTools.UnitTesting;
namespace?SingletonPattern.Test.Web
{
????public?partial?class?_Default?:?System.Web.UI.Page
????{
????????protected?void?Page_Load(object?sender,?EventArgs?e)
????????{
????????????Singleton?s1?=?Singleton.Instance;
????????????Singleton?s2?=?Singleton.Instance;
????????????//?確認(rèn)獲得的Singleton實(shí)例引用確實(shí)已經(jīng)被實(shí)例化了
????????????Assert.IsNotNull(s1);
????????????Assert.IsNotNull(s2);
????????????//?確認(rèn)兩個(gè)引用調(diào)用的是同一個(gè)Singleton實(shí)例
????????????Assert.AreEqual(s1.GetHashCode(),?s2.GetHashCode());
????????????//?顯示出當(dāng)前Singleton實(shí)例的標(biāo)識(shí),用于比較與其他
????????????//?HttpContext環(huán)境下的Singleton實(shí)例其實(shí)是不同的實(shí)例
????????????instanceHashCode.Text?=?s1.GetHashCode().ToString();
????????}
????}
}瀏覽器效果
同上,這段單元測(cè)試驗(yàn)證了Web Form下的細(xì)顆粒度Singleton,通過將唯一實(shí)例的存儲(chǔ)位置從當(dāng)前線程遷移到HttpContext,一樣可以實(shí)現(xiàn)細(xì)顆粒度的Singleton設(shè)計(jì)意圖。
更通用的細(xì)顆粒度Singleton
但如果你是一個(gè)公共庫或者是公共平臺(tái)的設(shè)計(jì)者,您很難預(yù)料到自己的類庫會(huì)運(yùn)行在Windows Form還是Web Form環(huán)境下,但Singleton模式作為很多公共機(jī)制,最常用的包括技術(shù)器、時(shí)鐘等等又常常會(huì)成為其他類庫的基礎(chǔ),尤其當(dāng)涉及到業(yè)務(wù)領(lǐng)域邏輯的時(shí)候,很難在開發(fā)過程就約定死運(yùn)行的模式。怎么辦?
這里借助一個(gè)工具類,通過它判斷當(dāng)前執(zhí)行環(huán)境是Web Form還是Windows Form,然后作一個(gè)2 in 1的細(xì)顆粒度Singleton(,聽起來有點(diǎn)象早年的任天堂游戲卡),不過就像我們提到的面向?qū)ο笤O(shè)計(jì)的單一職責(zé)原則一樣,把兩個(gè)和在一起會(huì)產(chǎn)生一些比較難看的冗余代碼,但Singleton與其他設(shè)計(jì)模式有個(gè)很顯著的區(qū)別——他不太希望被外部機(jī)制實(shí)例化,因?yàn)樗3謱?shí)例的唯一性,因此一些常用的依賴倒置技巧在這里又顯得不太適用。這里實(shí)現(xiàn)一個(gè)稍有些冗余的Web Form + Windows Form 2 in 1的細(xì)顆粒度Singleton如下:
UMLC# 工具類GenericContext
///?判斷當(dāng)前應(yīng)用是否為Web?應(yīng)用的Helper?方法(非官方方法)private?static?bool?CheckWhetherIsWeb()
{
????bool?result?=?false;
????AppDomain?domain?=?AppDomain.CurrentDomain;
????try
????{
????????if?(domain.ShadowCopyFiles)
????????????result?=?(HttpContext.Current.GetType()?!=?null);
????}
????catch?(System.Exception){}
????return?result;
}?
//C#?2in?1的細(xì)顆粒度Singleton模式實(shí)現(xiàn)using?System;
using?System.Web;
using?MarvellousWorks.PracticalPattern.Common;
namespace?MarvellousWorks.PracticalPattern.SingletonPattern.Combined
{
????public?class?Singleton
????{
????????private?const?string?Key?=?"marvellousWorks.practical.singleton";
????????private?Singleton()?{?}?????//?對(duì)外封閉構(gòu)造
????????[ThreadStatic]
????????private?static?Singleton?instance;
????????public?static?Singleton?Instance
????????{
????????????get
????????????{
????????????????//?通過之前準(zhǔn)備的GenericContext中非官方的方法
????????????????//?判斷當(dāng)前執(zhí)行模式是Web?Form還是非Web?Form
????????????????//?本方法沒有在?.NET?的?CF?和?MF?上驗(yàn)證過
????????????????if?(GenericContext.CheckWhetherIsWeb())?????//?Web?Form
????????????????{
????????????????????//?基于HttpContext的Lazy實(shí)例化過程
????????????????????Singleton?instance?=?(Singleton)HttpContext.Current.Items[Key];
????????????????????if?(instance?==?null)
????????????????????{
????????????????????????instance?=?new?Singleton();
????????????????????????HttpContext.Current.Items[Key]?=?instance;
????????????????????}
????????????????????return?instance;
????????????????}
????????????????else??//?非Web?Form方式
????????????????{
????????????????????if?(instance?==?null)
????????????????????????instance?=?new?Singleton();
????????????????????return?instance;
????????????????}
????????????}
????????}
????}
}
小結(jié)
設(shè)計(jì)模式中很多意圖部分表述的要求其實(shí)也都是有語意范圍 的,比如說“唯一”、“所有相關(guān)”、“一系列相互依賴的”等,但項(xiàng)目中往往有自己定制化的要求,可能的話建議盡量用語言、語言運(yùn)行環(huán)境的特性完成這些工作。
?原文地址:http://www.infoq.com/cn/articles/fine-grained-singleton-pattern
轉(zhuǎn)載于:https://www.cnblogs.com/jeriffe/articles/2142996.html
《新程序員》:云原生和全面數(shù)字化實(shí)踐50位技術(shù)專家共同創(chuàng)作,文字、視頻、音頻交互閱讀總結(jié)
以上是生活随笔為你收集整理的细颗粒度Singleton模式实现的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: code::blocks 修改代码字体颜
- 下一篇: PHP在各种HTTP服务器上运行模式分析