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