Autofac筆記2-淺談Singleton

開始前先聲明(坦白從寬~),我對Design Pattern的研究十分淺薄,寫起IoC、Singleton的題材有種越級打怪的心虛感,我知道本部落格有不少讀者深諳此道,如筆記有疑或有誤之處,懇請十方大德不吝指正。

Singleton是挺常見的設計模式,旨在確保該型別於Process中只會產生單一Instance(執行個體)。在.NET實現Singleton慣用的做法是將建構式設成private,另外宣告一個static屬性命名為Instance,在第一次get時建立物件,之後每次要取用該類別時不再重新建構,而是直接取用Instance屬性,如下例:

using System;
using System.Threading;
 
public class TheOne
{
    private Guid UniqueKey = Guid.NewGuid();
 
    private static TheOne instance = null;
    public static TheOne Instance
    {
        get
        {
            if (instance == null)
            {
                instance = new TheOne();
            }
            return instance;
        }
    }
 
    /// <summary>
    /// 建構式
    /// </summary>
    private TheOne()
    {
        Thread.Sleep(2000);
        Console.WriteLine("Constructor Executed");
    }
 
    public void ShowUniqueKey()
    {
        Console.WriteLine("Unique Key={0}", UniqueKey);
    }
 
}

應用時透過TheOne.Instance取得唯一的執行個體:

        static void Test1()
        {
            for (int i = 0; i < 3; i++)
            {
                TheOne theOne = TheOne.Instance;
                theOne.ShowUniqueKey();
            }
        }

執行後可驗證TheOne只被建構了一次,三次使用的都是同一Instance。

Constructor Executed
Unique Key=571842aa-5037-43db-9341-6e82f0ebe6d0
Unique Key=571842aa-5037-43db-9341-6e82f0ebe6d0
Unique Key=571842aa-5037-43db-9341-6e82f0ebe6d0

不過,老鳥們都知道上述寫法未考慮Thread-Safe,在多執行緒下肯定破功。以下我們就來踢爆這個"黑心Singleton"(誤):

        static void Test2()
        {
            for (int i = 0; i < 3; i++)
            {
                ThreadPool.QueueUserWorkItem((o) =>
                {
                    TheOne theOne = TheOne.Instance;
                    theOne.ShowUniqueKey();
                });
            }
        }

當改用ThreadPool以三個執行緒同時存取TheOne。很好! 建構式跑了三次,生出三個TheOne…

Constructor Executed
Constructor Executed
Unique Key=a3f52f2f-6097-4a90-8a26-5cb91b12c484
Constructor Executed
Unique Key=a06da108-c38b-43c9-aa01-04f5e261c121
Unique Key=37063ff3-990c-44a0-ac0a-798ea0bcf1ae

微軟有一篇很棒的文章詳細討論了.NET Singleton實作,如果要做到Thread-Safe,TheOne最好加上雙重檢查鎖定(Double-Check Locking)機制並改寫如下:

    private static TheOne instance = null;
    private static object syncRoot = new Object();
    public static TheOne Instance
    {
        get
        {
            if (instance == null)
            {
                lock (syncRoot)
                {
                    if (instance == null)
                        instance = new TheOne();
                }
            }
            return instance;
        }
    }

如此就能確保多執行緒下也只產生唯一的Instance。

Autofac提供了另一種實現Singleton的選擇,做法是在ContainerBuilder註冊型別時呼叫SngleInstance(),任何類別,不需特殊設計都能實現Singletone。我們另外宣告一個TheNewOne類別,功能與TheOne類似,但直接提供public的建構式,省去static Instace屬性,Singleton需求交給Autofac處理:

using System;
using System.Threading;
 
public class TheNewOne
{
    private Guid UniqueKey = Guid.NewGuid();
 
    public TheNewOne()
    {
        Thread.Sleep(2000);
        Console.WriteLine("Constructor Executed");
    }
 
    public void ShowUniqueKey()
    {
        Console.WriteLine("Unique Key={0}", UniqueKey);
    }
}

測試時先宣告ContainerBuilder,註冊TheNewOne型別並宣告SingleInstane(),後續使用時只需透過ResolveType<TheNewOne>()取得TheNewOne,就是Singleton了。

        static void Test3()
        {
            ContainerBuilder builder = new ContainerBuilder();
            //註冊時加註SingleInstance(),Autofac便會以Singleton方式提供物件
            builder.RegisterType<TheNewOne>().SingleInstance();
            IContainer container = builder.Build();
 
            for (int i = 0; i < 3; i++)
            {
                ThreadPool.QueueUserWorkItem((o) =>
                {
                    TheNewOne theOne = container.Resolve<TheNewOne>();
                    theOne.ShowUniqueKey();
                });
            }
        }

測試結果,類別不用特別加入Singleton邏輯就實現了多執行緒下的Singleton。

Constructor Executed
Unique Key=972def1e-8788-45aa-bd15-2aef15870514
Unique Key=972def1e-8788-45aa-bd15-2aef15870514
Unique Key=972def1e-8788-45aa-bd15-2aef15870514

【結論】透過IoC(Autofac)實現Singleton,相形之下比自己DIY簡便許多,但有個缺點: 由於類別建構式公開,無法禁止開發人員繞過Autofac自行另建Instance。但在實務上,若架構啟用Autofac或任何IoC,多會另外宣告Interface,開發人員依賴的是Interface而非Class本身,對底層究竟使用何Class理應處於"無知"狀態,越過Interface直接存取類別可視為"違法亂紀"的罪行,視為人員管理問題而非架構缺陷。依此前題,建構式公開就不算嚴重缺失,可安心服用。

歡迎推文分享:
Published 02 November 2013 11:20 PM 由 Jeffrey
Filed under:
Views: 9,885



意見

沒有意見

你的看法呢?

(必要的) 
(必要的) 
(選擇性的)
(必要的) 
(提醒: 因快取機制,您的留言幾分鐘後才會顯示在網站,請耐心稍候)

5 + 3 =

搜尋

Go

<November 2013>
SunMonTueWedThuFriSat
272829303112
3456789
10111213141516
17181920212223
24252627282930
1234567
 
RSS
創用 CC 授權條款
【廣告】
twMVC

Tags 分類檢視
關於作者

一個醉心技術又酷愛分享的Coding魔人,十年的IT職場生涯,寫過系統、管過專案, 也帶過團隊,最後還是無怨無悔地選擇了技術鑽研這條路,近年來則以做一個"有為的中年人"自許。

文章典藏
其他功能

這個部落格


Syndication