一個 Ambient Context 實作範例。
關於 Ambient Context
範例程式
如前面提過的,Ambient Context 模式可用於程式特定執行範圍內共享物件狀態,這個「特定範圍」可以是整個應用程式、特定執行緒、或其他自訂的執行範圍。這裡用 .NET Framework 4.0 之後提供的 ThreadLocal<T> 來示範如何實作一個依個別執行緒(per thread)共享物件資訊的 Ambient Context 類別。
令此類別名稱為 PerThreadContext,並且提供一個靜態的 Current 屬性,供外界取得當前執行緒的 context 物件。那麼,用戶端程式可以透過以下方式來取得當前 context 裡面的共享物件:
PerThreadContext 類別的程式碼如下:
再以一個簡單的 Console 程式來觀察其運作機制:
執行結果顯示,同樣是印出 PerThreadContext.Current.OnceUponATime 屬性值,不同的執行緒會有不同的結果。
也就是說,每次透過 PerThreadContext.Current 屬性所取得的 context 物件必然就是依附於當前執行緒的那一個 context 物件,與其他執行緒無關。
關於 Ambient Context
「環境脈絡」(ambient context)又稱為「環境物件」(context object),是一種常見的設計模式,主要用於跨階層、跨模組共享物件、界定程式執行區塊的範圍、以及提供橫切面的服務(cross-cutting concerns)。這些到處都需要的物件或服務,不太可能一一注入到每個需要它們的地方:一來太過繁瑣,二來有些子模組或程式區塊是根本碰觸不到、或不在控制範圍內的。因此,Ambient Context 不是侵入性的,而是在某個地方已經準備好、等著別人來取用。
像 .NET 基礎類別庫中用來建立交易範圍的 System.Transactions.TransactionScope 就是 Ambient Context 的一個應用例,還有 ASP.NET 的 Http.Web.HttpContext 也是。
範例程式
如前面提過的,Ambient Context 模式可用於程式特定執行範圍內共享物件狀態,這個「特定範圍」可以是整個應用程式、特定執行緒、或其他自訂的執行範圍。這裡用 .NET Framework 4.0 之後提供的 ThreadLocal<T> 來示範如何實作一個依個別執行緒(per thread)共享物件資訊的 Ambient Context 類別。
令此類別名稱為 PerThreadContext,並且提供一個靜態的 Current 屬性,供外界取得當前執行緒的 context 物件。那麼,用戶端程式可以透過以下方式來取得當前 context 裡面的共享物件:
var obj = PerThreadContext.Current.SomeMember;
PerThreadContext 類別的程式碼如下:
public class PerThreadContext { // 用一個靜態的 ThreadLocal<T> 來管理各執行緒的 context 物件。 private static ThreadLocal<PerThreadContext> _threadedContext; static PerThreadContext() { _threadedContext = new ThreadLocal<PerThreadContext>(); } // 共享的狀態 public DateTime OnceUponATime { get; private set; } // 把建構函式宣告為私有,不讓外界任意 context 物件。 private PerThreadContext() { OnceUponATime = DateTime.Now; } public static PerThreadContext Current { get { // 如果目前的執行緒中沒有 context 物件... if (_threadedContext.IsValueCreated == false) { // 就建立一個,並保存至 thread-local storage。 _threadedContext.Value = new PerThreadContext(); } return _threadedContext.Value; } } }
再以一個簡單的 Console 程式來觀察其運作機制:
static void Main(string[] args) { ShowTime(); System.Threading.Thread.Sleep(2000); var t1 = new Thread(ShowTime); var t2 = new Thread(ShowTime); t1.Start(); System.Threading.Thread.Sleep(2000); t2.Start(); System.Threading.Thread.Sleep(2000); ShowTime(); /* 執行結果: Thread 1: 2014/5/4 下午 01:37:09 Thread 3: 2014/5/4 下午 01:37:11 Thread 4: 2014/5/4 下午 01:37:13 Thread 1: 2014/5/4 下午 01:37:09 */ } static void ShowTime() { Console.WriteLine("Thread {0}: {1} ", Thread.CurrentThread.ManagedThreadId, PerThreadContext.Current.OnceUponATime); }
執行結果顯示,同樣是印出 PerThreadContext.Current.OnceUponATime 屬性值,不同的執行緒會有不同的結果。
也就是說,每次透過 PerThreadContext.Current 屬性所取得的 context 物件必然就是依附於當前執行緒的那一個 context 物件,與其他執行緒無關。
沒有留言: