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

posted @ 2014-01-12 16:37  niaomingjian  阅读(4736)  评论(0编辑  收藏  举报