使用 [MethodImpl(MethodImplOptions.Synchronized)] 或 lock 修飾 Instance 方法後,四執行緒並發測試最終計數穩定為 16,無重複初始化問題。每次執行順序因排程不同而異,但結果正確。
public sealed class Singleton {
private static readonly Lazy<Singleton> lazy =
new Lazy<Singleton>(() => new Singleton(), LazyThreadSafetyMode.ExecutionAndPublication);
private Singleton() { }
public static Singleton Instance { get { return lazy.Value; } }
}
優點:語意清晰、效能優異、支援例外快取(建構失敗後重試)、可序列化。
public sealed class Singleton {
private static readonly Singleton instance;
static Singleton() { instance = new Singleton(); } // CLR 保證執行一次
private Singleton() { }
public static Singleton Instance { get { return instance; } }
}
限制實例數量上限為 N(如資料庫連線池、執行緒池):
public sealed class MultiTon {
private static readonly Dictionary instances = new Dictionary();
private static readonly object syncLock = new object();
private int id;
private MultiTon(int id) { this.id = id; }
public static MultiTon GetInstance(int key) {
string k = key.ToString();
lock (syncLock) {
if (!instances.ContainsKey(k))
instances[k] = new MultiTon(key);
return instances[k];
}
}
public int Id { get { return id; } }
}
現代架構建議由容器管理生命週期,而非類別自行實作單例:
// ASP.NET Core 範例
services.AddSingleton<IMyService, MyService>(); // 容器保證單例
// 建構子注入
public class Consumer {
private readonly IMyService service;
public Consumer(IMyService service) { this.service = service; }
}
| 問題 | 說明 | 緩解 |
|---|---|---|
| 隱藏依賴 | 類別間耦合不透明 | 介面抽象 + DI 容器 |
| 難以單元測試 | 狀態殘留、難以 Mock | 改用 DI、提供 Reset() |
| 全域狀態副作用 | 平行測試干擾 | 避免可變靜態狀態 |
| 序列化破壞單一性 | 反序列化產生新實例 | 實作 ISerializable 控制 |