2012-08-27 22 views
1

我有一些非託管內存結構用於與C++ dll進行通信。 每個這樣的結構都必須手動釋放,所以我把它包裝在MyUnmanagedStructure中,它實現了IDisposable在終結器中處理一個IDisposables列表

我總是需要這些結構的可變數量在一起,所以我有一個集合MyUnmanagedStructureCollection它也實現了IDisposable。

(見下面的小例子代碼)

只要我的媒體庫的用戶總是調用Dispose()或包裝與using() {}是沒有問題的集合,但我不能保證。即使用戶不手動處理收集,我也不想泄漏內存。

MyUnmanagedStructureCollection.Dispose()方法被垃圾回收通過終結器調用時,那麼據我所知我不能確定我的private List<MyUnmanagedStructure>還沒有被垃圾回收,那麼我怎麼能在這種情況下處理每個結構呢?

在我的最終代碼中,我是否應該嘗試迭代列表,希望它尚未被垃圾收集?

在try/catch塊中執行此操作是否是一種很好的做法,捕獲ObjectDisposedException?

或者我應該讓每個非託管結構「爲自己謀生」,依靠單獨的終結器,並且在我的集合的終結器中完全不做任何事情?

public class MyUnmanagedStructureCollection : IDisposable 
{ 
    private List<MyUnmanagedStructure> structures; 
    private bool disposed = false; 

    #region standard IDIsposable pattern 
    public ~MyUnmanagedStructureCollection() 
    { 
     this.Dispose(false); 
    } 

    public void Dispose() 
    { 
     this.Dispose(true); 
     GC.SuppressFinalize(this); 
    } 

    protected virtual void Dispose(bool disposing) 
    { 
     if (!disposed) 
     { 
      // Dispose unmanaged resources 
      // Should not access managed resources, 
      // the garbage collection may have claimed them already! 

      // PROBLEM!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 
      // this.structures is a List<MyUnmanagedStructure>; so is a managed resource!!! 

      foreach (var structure in this.structures) 
       structure.Dispose(disposing) 
      this.removeAllMemoryPressure(); 

      if (disposing) 
      { 
       // Dispose managed resources. 
       this.structures.Clear(); 
       this.structures = null; 
      } 

     } 
     disposed = true; 
    } 
} 


public class MyUnmanagedBuffer : IDisposable 
{ 
... 
} 
+1

當它仍然從您的對象引用時,如何收集列表?只是收集中的對象可能已經完成(但未收集)。 – CodesInChaos

+0

那麼,我的對象和對象內的列表將被收集在一起,並且據我瞭解,GC並不能保證它以何種順序銷燬它們。我可能是錯誤的,雖然 – HugoRune

+0

GC並不保證*定稿*的順序。它保證從終結器到達的任何對象尚未被收集。 – CodesInChaos

回答

2

的GC的工作方式是:

  1. 找到所有到達的對象
  2. 排隊了終結所有可達對象到終結隊列
  3. 馬克是到達從所有對象定稿隊列爲可達
  4. 釋放剩餘的不可觸及對象

從終結器運行的對象引用的對象不能被垃圾回收。

您需要小心的唯一的事情就是未定義完成的順序。所以列表中的元素可能已經完成,但沒有收集。最終確保是單線程的,所以你也需要鎖定。

人們通常會試圖避免這樣的定稿鏈,因爲獨立定稿更簡單。但是如果有些物體需要先於其他物體處理,那麼這樣的構造是不可避免的。

您還應該考慮使用SafeHandle s進行關鍵的最終確定。


可達性

之一爲最終確定的準則是,Finalize方法不應該接觸其他對象。人們有時會錯誤地認爲這是因爲那些其他物體已經被收集。然而,正如我已經解釋過的那樣,可升級對象的整個可達圖形被提升。

準則的真正原因是爲了避免接觸可能已經完成的對象。這是因爲定稿是無序的。

Chris Brumme on finalization

+0

我的擔心詞幹構成了我已閱讀的有關IDisposable模式的常見建議,例如[here](http:/ /stackoverflow.com/questions/538060/proper-use-of-the-idisposable-interface)。我讀的所有東西聲稱我不應該從我的終結器訪問管理對象。從我的終結器訪問受管列表是否真的存檔? – HugoRune

+0

非常感謝,您的更新很好地解決了這個問題。關於你對SafeHandle的建議,我閱讀了他們,但到目前爲止我無法弄清楚如何使用它們,因爲創建這個非託管對象的代碼在C-Dll中,並且我只在C#端獲得一個指針(和我必須用這個指針在C-DLL中調用一個函數,以便在最後釋放它) – HugoRune

+0

微小的技術問題:無論出於何種原因,已經註冊了終結器的對象列表的名稱,但是沒有被標記爲立即完成是「定稿隊列」。被發現不可訪問的對象列表是「freachable」隊列。 (來自http://msdn.microsoft.com/en-us/magazine/bb985010.aspx的信息)。名稱似乎比較落後,但據我所知,該網站是正確的。 – supercat

1

既然你MyUnmanagedBuffer類是從點的視圖您MyUnmanagedStructureCollection類的管理資源,我覺得應該把它處理這樣的。這意味着MyUnmanagedStructureCollection.Dispose(bool)方法如下。

protected virtual void Dispose(bool disposing) { 
    if (!disposed) { 
     // Dispose unmanaged resources 
     // Should not access managed resources, 
     // the garbage collection may have claimed them already! 

     if (disposing) { 
      // Dispose managed resources. 
      // This means that we try to dispose all items in the structures collection. 
      if (this.structures != null) { 
       foreach (var structure in this.structures) { 
        structure.Dispose(disposing); 
        this.removeAllMemoryPressure(); // What does this? 
       } 
       this.structures.Clear(); 
       this.structures = null; 
      } 
     } 
    } 

    disposed = true; 
} 
+0

removeAllMemoryPressure()調用'GC.RemoveMemoryPressure(this.memoryPressure)',我不確定我應該在這個例子中提及。每當我添加一個UnmanagedStructure時,我都會調用GC.AddMemoryPressure(SizeOfStructure)使GC將這個大小考慮在內。所以我最終需要刪除完全相同數量的記憶壓力,無論我的課程是如何處理的 – HugoRune