簡體   English   中英

處理 singleton 實例 (C#)

[英]Disposing a singleton instance (C#)

如果 singleton 實現 IDisposable,那么處置和重新創建實例的正確方法是什么? 一種方法是保留 _disposed 標志並在 Instance 屬性中檢查它,但我不確定這是正確的方法。 一個簡單的例子:


public sealed class Singleton: IDisposable
{
   private static Singleton _instance;
   private object _lock;
   private UnmanagedResource _unmanaged;
   private bool _disposed;

   private Singleton()
   {
      _unmanaged = new UnmanagedResource();
      _disposed  = false;
      _lock      = new object();
   }

   public UnmanagedResource Unmanaged { get { return _unmanaged; } }

   public static Singleton Instance
   {
      get
      {
         if (_instance == null || _disposed)
         {
            lock (_lock)
            {
               if (_instance == null || _disposed)
               {
                  _instance = new Singleton();
               }
            }
         }
         return _instance;
      }
   }

   public void Dispose()
   {
      _disposed = true;
      try
      {
         _unmanaged.Dispose();
      }
      finally
      {
         GC.SuppressFinalize(this);
      }
   }
}

所以像這樣的代碼是可能的(雖然,是的,我同意,它有點違背了擁有單例的目的):


Singleton.Instance.Dispose();
Singleton.Instance.Unmanaged.UseResource();   // Unmanaged shouldn't be null.

注意: Singleton 和 IDisposable 之間的不兼容不用過分強調,我理解。 當 ApppDomain 卸載時,我需要 Dispose 方法來釋放非托管資源。 如果您對名為 Singleton 的 class 有疑問,我可以將其重命名為 LoadBalancer。 問題仍將保持不變。 這個 LoadBalancer 需要是一次性的,因為它的實例不屬於任何人,但應該妥善處理。

Singleton 和 Disposable 對象在很大程度上是不兼容的概念

  • 單例是一種類型的單個實例,在應用程序的整個生命周期內都可用
  • 一次性對象是那些不再使用后需要及時處理資源的對象。

單例通常在進程/ AppDomain的整個生命周期內都是活躍的。 如果您發現自己想要Dispose它們,那么您可能需要稍微重構您的解決方案。

如果問題與在AppDomain卸載期間釋放資源有關,那么將釋放資源的責任與負責管理AppDomain生命周期的 object 相同。 然后將此資源嵌入Singleton中而不實現IDisposable 這將適當地分離出場景的關注點。

如果您需要更換 singleton 的實例,您應該考慮您的設計。

如果你真的認為你應該這樣做,我建議使用 2 個對象......

一個 singleton object 充當代理,並且是真正的 singleton(生命周期結束 == 進程結束)

這個 object 需要公共成員來完成您的 singleton 所能做的一切,並且需要一個私人成員持有真正的實施 object。 所有其他成員重定向到該實現的成員。

第二個object是可更換的一次性object。 make sure that only your singleton object will ever hold a reference on that... this way it doesn't matter if any consumer object holds a reference on the singleton that would prevent you from replacing the object... that ref will only point到代理

我沒有看到這將如何工作。 考慮這個假設的用例:

using (Singleton instance = Singleton.Instance)
{
  // Do stuff with the instance.
}

您將如何防止多個線程同時執行此代碼? 一個線程可以調用Dispose ,而另一個線程仍在嘗試使用同一個實例。 嘗試將具有循環語義的 API 強制轉換為 singleton 概念就像試圖將方形釘子安裝在圓形支架中。

順便說一句,還有一些切向的問題值得一提。 Instance屬性不是線程安全的。 至少您必須將_instance標記為volatile 只有當您使用該模式的規范實現時。 您將永遠無法使模式安全,因為您還使用_disposed標志作為檢查中的額外標准。 就個人而言,我會完全放棄雙重檢查鎖定模式。

也許我在問一個愚蠢的問題,但你為什么要處理 Singleton 只是為了創建一個新的? 目的不是只有一個實例嗎......

這可能是我不知道的設計問題。 在這種情況下,也許“正確的方法”是重新評估你需要你的代碼做什么以及什么樣的模式讓你到達那里。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM