一個 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 物件,與其他執行緒無關。
沒有留言: