• 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

  • 相关阅读:
    ubuntu环境下编译内核详解(linux kernel compile)
    35个你也许不知道的Google开源项目
    Android系统匿名共享内存Ashmem(Anonymous Shared Memory)简要介绍和学习计划
    Android应用程序启动过程源代码分析
    Android应用程序在新的进程中启动新的Activity的方法和过程分析
    Android应用程序的Activity启动过程简要介绍和学习计划
    Android系统匿名共享内存Ashmem(Anonymous Shared Memory)在进程间共享的原理分析
    Android系统在新进程中启动自定义服务过程(startService)的原理分析
    Android系统进程间通信Binder机制在应用程序框架层的Java接口源代码分析
    Android应用程序内部启动Activity过程(startActivity)的源代码分析
  • 原文地址:https://www.cnblogs.com/niaomingjian/p/3516083.html
Copyright © 2020-2023  润新知