Per-Thread Ambient Context 範例

一個 Ambient 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 物件,與其他執行緒無關。

沒有留言:

技術提供:Blogger.
回頂端⬆️