適用於 .NET Framework 早期版本,需搭配 volatile 防止指令重排序導致執行緒讀到未完全初始化的實例。
public sealed class Singleton where T : class, new()
{
private static volatile T _instance = null;
private static readonly object _lock = new object();
private Singleton() { }
public static T Instance
{
get
{
if (_instance == null) // 第一次檢查 (無鎖,效能佳)
{
lock (_lock) // 同步鎖
{
if (_instance == null) // 第二次檢查 (防止多執行緒同時通過第一次檢查)
_instance = new T();
}
}
return _instance;
}
}
// 手動重置實例 (如配置熱更新)
public static void RefreshInstance()
{
lock (_lock)
{
_instance = new T();
}
}
}
| 關鍵字 | 作用 |
|---|---|
volatile | 確保 `_instance` 讀寫不被 CPU/編譯器重排序,保證其他執行緒看到完全初始化後的物件。 |
readonly object _lock | 專用鎖物件,避免 `lock(this)` 或 `lock(typeof(T))` 造成死鎖風險。 |
sealed + 私有建構子 | 防止繼承與外部實例化,強制單例。 |
| 雙重檢查 | 第一次無鎖判斷提升高併發效能;第二次在鎖內確保只建立一次。 |
System.Lazy 內建執行緒安全、延遲初始化、效能優化,程式碼極度精簡:
public sealed class Singleton where T : class, new()
{
private static readonly Lazy _lazy = new Lazy(() => new T(), LazyThreadSafetyMode.ExecutionAndPublication);
private Singleton() { }
public static T Instance => _lazy.Value;
public static void RefreshInstance()
{
// Lazy 無法直接重置,需重新建立 Lazy 實例 (罕見需求)
// 若必須支援重置,建議維持經典寫法或自行實作可重置的 Lazy 包裝器
}
}
LazyThreadSafetyMode.ExecutionAndPublication:確保多執行緒下僅執行一次初始化,且發佈可見性正確 (預設值)。volatile、lock,框架層級保證正確性。現代 ASP.NET Core 應用不建議手寫 Singleton,直接註冊服務生命週期:
// Program.cs / Startup.cs
services.AddSingleton(); // 容器管理單例生命週期
優點:支援建構子注入、單元測試易 Mock、生命週期由容器統一管理。
AddSingleton。Lazy 簡潔安全。volatile + 雙重檢查鎖定。Singleton 非銀彈,濫用會導致強耦合、難測試、隱藏依賴,請審慎評估。