• 托管资源,非托管资源,资源释放 IDisposable


    能在.Net Framework中找到的类产生的对象,都是 托管资源。自定义的类型需要注意是否用到了非托管资源。

    非托管资源 则是CLR无法对这些资源管理,这些资源的申请、释放必须由使用者自行管理。例如,像Win32 API编程中的文件句柄,上下文句柄、窗口、GDI+对象、COM对象、数据库连接或网络连接等(P/Invoke)资源、StreamReader等各种流都属于非托管资源。

    资源释放,一般指的是非托管资源释放,托管资源由GC自动管理。

    终结器 应用 备注
    ~ClassName(){} 必须 确保释放非托管资源,GC会隐式调用
    Finalize(){} 尽量不用 很像C++的析构函数

    在Finalize方法中应该尽量避免引用其他实现了Finalize方法的对象,减少依赖性问题。
    一个实现了Finalize方法的对象必需等两次GC才能被完全释放,可见,这种“自动”释放资源的方法并不能满足我们的需要,因为我们不能显示的调用它(只能由GC调用),而且会产生依赖型问题。我们需要更准确的控制资源的释放。
    以此代码中最好避免使用终结器,也应尽量少让代码的逻辑使用到终结器。

    Dispose是提供给我们显示调用的方法。使用using()调用,能自动调用对象Dispose方法。Dispose一般实现模式如下:
    IDisposiable是显示释放对象的接口,实现IDisposiable接口的类,可以显式地释放对象。

    /// <summary>
    /// 含有非托管资源的类,继承Dispose
    /// </summary>
    public class DisposePattern : IDisposable
    {
        // 是否释放,true已释放
        private bool _isDisposed = false;
        // 锁对象
        private static readonly object _clockObj = new object();
        // 非托管资源
        private System.IO.FileStream fs = new System.IO.FileStream("test.txt", System.IO.FileMode.Create);
        // 托管资源
        private List<string> data = new List<string>();
    
        // 包含非托管资源的类才有意义,防止使用者没有显示调用Dispose方法
        ~DisposePattern()
        {
            // false时,只处理非托管资源即可,托管资源会自动清理
            Dispose(false);
        }
        // 显示调用
        public void Dispose()
        {
            // true时要同时处理托管资源、非托管资源
            Dispose(true);
            //告诉GC不需要再调用Finalize方法,
            //因为资源已经被显示清理
            GC.SuppressFinalize(this);
        }
        // 实际释放资源的地方
        // 受保护的虚方法,考虑到这个类型被继承时,子类也许会实现自己的Disponse。这时子类要调用base.Disponse。
        protected virtual void Dispose(bool disposing)
        {
            //不需要多次释放
            if (_isDisposed) return;
            //由于Dispose方法可能被多线程调用,所以加锁以确保线程安全
            lock (_clockObj)
            {
                if (disposing)
                {
                    //说明对象的Finalize方法并没有被执行,在这里可以安全的引用其他实现了Finalize方法的对象
                    //释放托管资源,包括事件监听
                    if (data != null)
                    {
                        data.Clear();
                        data = null;
                    }
                }
                //释放非托管资源
                if (fs != null)
                {
                    fs.Dispose();
                    fs = null; //标识资源已经清理,避免多次释放
                }
                // 让类型知道自己已经被释放
                _isDisposed = true;
            }
        }
    
    }
    
    /// <summary>
    /// 含有非托管资源的派生类,基类实现了Dispose
    /// </summary>
    public class DerivedClass : DisposePattern
    {
        // 是否释放
        private bool _isDisposed = false;
        // 锁对象
        private static readonly object _clockObj = new object();
        protected override void Dispose(bool disposing)
        {
            //不需要多次释放
            if (_isDisposed) return;
            lock (_clockObj)
            {
                try
                {
                    //清理自己的资源,实现模式与DisposePattern相同
                    if (disposing)
                    {
                        //释放托管资源
                    }
                    //释放非托管资源
                }
                finally
                {
                    //基类释放自己的资源
                    base.Dispose(disposing);
                    _isDisposed = true;
                }
            }
        }
    
    }
    

    当然,如果DerivedClass本身没有什么资源需要清理,那么就不需要重写Dispose方法。

  • 相关阅读:
    如何选择Linux操作系统版本?
    js+html实现玫瑰花绽放
    Linux系统目录结构
    laravel5.6操作数据curd写法(查询构建器)
    laravel5.6 常规框架部署和配置文件说明
    PHP读取XML文件数据获取节点值
    Fiddler正则匹配调试接口示例
    php常用端口号
    php heredoc的用法详解
    oracle表空间操作
  • 原文地址:https://www.cnblogs.com/wesson2019-blog/p/12767631.html
Copyright © 2020-2023  润新知