• C#基础笔记——资源管理


    一、概述资源管理

    什么是C#(或者说是.NET)的资源?

    简单的说C#的每一种类型都代表一种资源。而资源又分为两类:

    托管资源:由CLR管理分配和释放发资源,即从CLR里new出来的对象。

    非托管资源:不受CLR管理的对象,如:Windows内核对象、文件、数据库连接、套接字和COM对象等。

    如若使用非托管资源可以通过两种方式释放资源:

    1.通过析构函数(Finalizer)来释放资源,.NET Framework提供了一个Object.Finalize方法,它允许在一个对象被GC要求回收它,所使用的内存时,正确清理非托管资源,但是它的使用有损性能。

    2.继承IDisposable接口,如果使用了expensive的非托管资源,则推荐使用显式释放资源继承IDisposable接口方法。

    二、Dispose模式实现托管资源显式释放和非托管资源释放实现

        public class SampleClass : IDisposable
        {
            
            private IntPtr nativeResource = Marshal.AllocHGlobal(100);//创建一个非托管资源,IntPtr:一个句柄平台特定类型
            private AnotherResource managedResource = new AnotherResource();//创建一个托管资源
            private bool disposed = false;// 声明一个处理标识
    
            /// <summary>
            /// 实现IDisposable接口中的Dispose方法
            /// </summary>
            public void Dispose()
            {
                Dispose(true);
                //通知垃圾回收机制GC不再调用终结器(析构器)
                GC.SuppressFinalize(this);
            }
    
            public void Close()
            {
                Dispose();
            }
    
            /// <summary>
            /// 必须方法,阻止程序猿忘记了显式调用Dispose方法
            /// ~SampleClass()被成为终结器(析构器)
            /// </summary>
            ~SampleClass()
            {
                Dispose(false);
            }
            protected virtual void Dispose(bool disposing)
            {
                if (disposed)
                {
                    return;
                }
                if (disposing)
                {
                    //清理托管代码
                    if (managedResource!=null)
                    {
                        managedResource.Dispose();
                        managedResource = null;
                    }
                    if (nativeResource!=null)
                    {
                        Marshal.FreeHGlobal(nativeResource);
                        nativeResource = IntPtr.Zero;
                    }
                    //类型知道自己已经被释放
                    disposed = true;
                }
            }
    
            public void SamplePublicMethod()
            {
                if (disposed)
                {
                    throw new ObjectDisposedException("SampleClass", "SampleClass is disposed. ");
                }
            
            }
        }
    
        public class AnotherResource
        {
            internal void Dispose()
            {
                //
            }
        }

    上面的代码给我们留下了如下疑问:

    1. 如果类型需要显式释放资源,那么一定要继承IDispose接口吗?

      答案是肯定的

    2. 我们如何使用SampleClass并让他在合适的时候释放呢?

      这里我推选使用语法糖using,编译器会自动为我们生产调用Dispose方法的IL代码:

     using (SampleClass sampleClass = new SampleClass())
     {
          //
     }

      在IL中上面的代码相对于:

    SampleClass sampleClass;
    try 
    {
        sampleClass=new  SampleClass();
        //
    }
    finally
    {
         sampleClass.Dispose();
    }    

      如果存在两个类型一致的对象,可以按照以下形式完成:

    using (SampleClass sampleClass = new SampleClass(),sampleanothorClass = new SampleClass())
    {
      //
    }

      如果两个类型不一致,则可以按照如下形式使用:

    using (SampleClass sampleClass = new SampleClass())
    using (SampleAnothorClass sampleAnothorClass = new SampleAnothorClass())
    {
         //
    }

    3.~SampleClass()有什么作用?

      该方法是终结器,基于终结器会被垃圾回收器调用的特点,它会用作资源释放的补救措施。我们知道在.NET中每次使用new创建对象时,CLR会在堆上分配内存,一旦对象不在被引用,就会回收他们的内存。对于没有继承IDisposable接口的类对象,垃圾回收器直接释放对象所在内存;而实现了Dispose模式的类型,在每次创建对象的时候,CLR都会将该对象的指针放在终结列表中,垃圾回收器在回收该对象内存前,会首先将终结列表中的指针放到一个freachable队列中。同时CLR会分配专门的线程读取freachable队列内容,并调用对象的终结器(如:.~SampleClass()),这时对象才真正被标识为垃圾,并在下次进行垃圾回收时释放对象占用内存。

    4. Dispose方法是否可以被多次调用,若多次调用是否会因为首次已经释放资源引起异常?

      这里的Dispose方法是可以被多次调用的,为了避免重复调用一起的异常我们引入了private bool disposed = false,在首次是否资源后将disposed赋予true值,再次被调用时直接做空返回,不会再次执行释放资源代码。

    5. 为什么Dispose模式提取了一个受保护的虚拟方法?

      这里我考虑到SampleClass类可能被当作基类继承,这样父类也和子类同样需要使用Dispose方法因此定义为virtual方法。防止子类遗忘对基类的Dispose调用,所以使用protected类型。若子类继承父类的Dispose模式可以通过base.Dispose(disposing) 方式基类释放资源。

    6. 为什么不用null赋值的形式进行资源释放呢?

      null针对值类型变量是可以做到资源释放的,但是对于引用类型没有实际意义,值为空但是内存空间仍然存在。

  • 相关阅读:
    蓝桥杯 包子凑数(完全背包、裴蜀定理、赛瓦维斯特定理)
    AcWing 255. 第K小数主席树(可持久化线段树) 模板题
    HDU 1698 Just a Hook [线段树+区间修改为一个值+区间查询]
    POJ 3264 Balanced Lineup
    AcWing 1277. 维护序列
    HDU 3577 Fast Arrangement [线段树+区间修改+维护最大值]
    【学习笔记】权值线段树
    NOIP2017 小凯的疑惑 解题报告(赛瓦维斯特定理)
    k8s (kubenetes)集成runner 清明
    gitlab CIDI 构建环境优化 清明
  • 原文地址:https://www.cnblogs.com/Abel-Zhang/p/ResourceManagement.html
Copyright © 2020-2023  润新知