C# 釋放非托管資源


    C#中資源分為托管資源和非托管資源。 托管資源由垃圾回收器控制如何釋放,不需要程序員過多的考慮(當然也程序員也可以自己釋放)。 非托管資源需要自己編寫代碼來釋放。那么編寫好的釋放非托管資源的代碼(釋非代碼)由誰來調用呢。有兩種實現方式:

     一 將釋非代碼放到構造函數析構函數中,由系統自動調用,系統會在資源對象不再使用了,會在某個時間調用構造函數析構函數來釋放非托管資源。構造函數析構函數的目的就是用來釋放或清理非托管資源的。但它有一個問題是調用的時間是系統說了算,不能在程序中自己想要調用時調用析構函數,這是C#規定的。那么就產生了第二種方式。

     二 將釋非代碼放到另外一個函數中,當自己想調用時就調用。將釋非代碼放在一個方法中共用,代碼如下:

 1 MyClass
 2 {
 3     ~MyClass()
 4     {
 5         DisposePro();
 6     }
 7 
 8     public void Dispose()
 9     {
10         DisposePro();
11     }
12     
13     private void DisposePro()
14     {
15         // 釋非代碼
16         ......
17     }
18 }

    但是這樣可能會產生其他問題。資源可能會被多次釋放,而產生問題。系統會自動調用析構函數,自己也可能多次調用Dispose()方法。那么解決方法是使用一個全局變量作為標記,來標記資源是否已經被釋放,已經釋放就不再釋放。代碼如下:

 1   MyClass
 2   {
 3       private bool disposed = false;
 4       ~MyClass()
 5       {
 6           DisposePro();
 7       }
 8   
 9       public void Dispose()
10       {
11          DisposePro();
12       }
13      
14      private void DisposePro()
15      {
16         if(disposed == false)
17         {
18          // 釋非代碼
19          ......
20         }
21          disposed = true;
22      }
23  }

    這樣看起來似乎沒有問題了。但是當調用Dispose()方法只能立即釋放非托管資源,而托管資源還是需要由GC自動處理。那么為了能夠做到調用Dispose()方法時也能夠釋放立即釋放托管資源,則需要在DisposePro()方法中添加上想要釋放的托管資源的釋放代碼(釋放托管代碼)。代碼如下:

 1     MyClass
 2     {
 3         private bool disposed = false;
 4         ~MyClass()
 5         {
 6             DisposePro();
 7         }
 8     
 9         public void Dispose()
10         {
11           DisposePro();
12         }
13        
14        private void DisposePro()
15        {
16           if(disposed == false)
17           {
18              // 釋托管代碼
19               ......
20              // 釋非代碼
21              ......
22           }
23            disposed = true;
24        }
25    }

    這樣還是有問題,當析構函數調用DisposePro()時,會調用釋托管代碼,可能產生問題——托管對象可能已經被GC刪除了而產生問題。那么使用一個標記給DisposePro(),當是被析構函數調用時不執行釋托管代碼。重新命名DisposePro(),代碼如下:

 1       MyClass
 2       {
 3           private bool disposed = false;
 4           ~MyClass()
 5           {
 6               Dispose(false);
 7           }
 8       
 9          public void Dispose()
10          {
11              Dispose(true);
12          }
13         
14         private void Dispose(bool disposing)
15         {
16            if(disposed == false)
17            {
18                 if(disposing == true)
19                 {
20                    // 釋托管代碼
21                    ......
22                 }
23               // 釋非代碼
24               ......
25            }
26            disposed = true;
27         }  
28      }

    用這段代碼來釋放資源應該沒有問題了。看一下標准清理模式,代碼如下:

 1     MyClass:IDisposable  2     {
 3         private bool disposed = false;
 4         ~MyClass()
 5         {
 6             Dispose(false);
 7         }
 8     
 9         public void Dispose()
10         {
11            Dispose(true);
12            GC.SuppressFinalize(this);
13         }
14        
15        private void Dispose(bool disposing)
16        {
17           if(disposed == false)
18           {
19                if(disposing == true)
20                {
21                   // 釋托管代碼
22                   ......
23                }
24               // 釋非代碼
25              ......
26           }
27           disposed = true;
28        }
29    }

    標准清理模式中多了一句GC.SuppressFinalize(this);【該方法通知CLR不要調用該方法的析構函數,因為它已經被清理了。】如果沒有這句代碼,我認為不影響程序的正確性,不會發生安全問題,他只是告訴系統不要再調用構造函數了。那么為什么要加上這句代碼呢?如果在調用了Dispose()之后再調用析構函數只是多此一舉,所以告訴系統不要再調用了。這一點應該和性能有關系。【如果不需要構造函數就不要執行構造函數,他們會帶來性能上的開銷】。

參考:

C#4.0 圖解教程 Daniel M. Solis

相關鏈接:

1. 實現 Finalize 和 Dispose 以清理非托管資源 http://msdn.microsoft.com/zh-cn/library/b1yfkh5e(v=vs.90).aspx

2. GC.SuppressFinalize 方法 http://msdn.microsoft.com/zh-cn/library/system.gc.suppressfinalize(v=vs.90).aspx


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM