zoukankan      html  css  js  c++  java
  • 清理未托管资源

    在类型使用者忘记调用 Dispose 的情况下,使用安全句柄包装非托管资源。 这是推荐采用的方法。 安全句柄派生自 System.Runtime.InteropServices.SafeHandle 抽象类,并包含可靠的 Finalize 方法。 在使用安全句柄时,只需实现 IDisposable 接口并在 Dispose 实现中调用安全句柄的 IDisposable.Dispose 方法。 如果未调用安全句柄的 Dispose 方法,则垃圾回收器将自动调用安全句柄的终结器。

    Microsoft.Win32.SafeHandles 命名空间中的以下派生类提供安全句柄:

    Dispose() 

    IDisposable 接口需要实现单个无参数的方法 Dispose。 此外,任何非密封类都应具有要实现的附加 Dispose(bool) 重载方法

    无参数的 Dispose 方法由该类型的使用者调用,因此其用途是释放非托管资源,执行常规清理,以及指示终结器(如果存在)不必运行。 释放与托管对象关联的实际内存始终是垃圾回收器的域。 因此,它具有标准实现:

    public void Dispose()
    {
       // Dispose of unmanaged resources.
       Dispose(true);
       // Suppress finalization.
       GC.SuppressFinalize(this);
    }

    disposing 参数是一个 Boolean,它指示方法调用是来自 Dispose 方法(其值为 true)还是来自终结器(其值为 false)。

    方法的主体包含两个代码块:

    • 释放非托管资源的块。 无论 disposing 参数的值如何,都会执行此块。

    • 释放托管资源的条件块。 如果 disposing 的值为 true,则执行此块。 它释放的托管资源可包括:

      • 实现 IDisposable 的托管对象。 可用于调用其 Dispose 实现(级联释放)的条件块。 如果你已使用 System.Runtime.InteropServices.SafeHandle 的派生类来包装非托管资源,则应在此处调用 SafeHandle.Dispose() 实现。

      • 占用大量内存或使用短缺资源的托管对象。 将大型托管对象引用分配到 null,使它们更有可能无法访问。 相比以非确定性方式回收它们,这样做释放的速度更快。

    如果方法调用来自终结器,则应仅执行释放非托管资源的代码。 实施者负责确保假路径不会与可能已被回收的托管对象交互。 这一点很重要,因为垃圾回收器在终止期间销毁托管对象的顺序是不确定的。

    使用安全句柄实现释放模式

    using Microsoft.Win32.SafeHandles;
    using System;
    using System.Runtime.InteropServices;
    
    public class DisposableStreamResource : IDisposable
    {
        // Define constants.
        protected const uint GENERIC_READ = 0x80000000;
        protected const uint FILE_SHARE_READ = 0x00000001;
        protected const uint OPEN_EXISTING = 3;
        protected const uint FILE_ATTRIBUTE_NORMAL = 0x80;
        private const int INVALID_FILE_SIZE = unchecked((int)0xFFFFFFFF);
    
        // Define Windows APIs.
        [DllImport("kernel32.dll", EntryPoint = "CreateFileW", CharSet = CharSet.Unicode)]
        protected static extern SafeFileHandle CreateFile(
            string lpFileName, uint dwDesiredAccess,
            uint dwShareMode, IntPtr lpSecurityAttributes,
            uint dwCreationDisposition, uint dwFlagsAndAttributes,
            IntPtr hTemplateFile);
    
        [DllImport("kernel32.dll")]
        private static extern int GetFileSize(
            SafeFileHandle hFile, out int lpFileSizeHigh);
    
        // Define locals.
        private bool _disposed = false;
        private readonly SafeFileHandle _safeHandle;
        private readonly int _upperWord;
    
        public DisposableStreamResource(string fileName)
        {
            if (string.IsNullOrWhiteSpace(fileName))
            {
                throw new ArgumentException("The fileName cannot be null or an empty string");
            }
    
            _safeHandle = CreateFile(
                fileName, GENERIC_READ, FILE_SHARE_READ, IntPtr.Zero,
                OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, IntPtr.Zero);
    
            // Get file size.
            Size = GetFileSize(_safeHandle, out _upperWord);
            if (Size == INVALID_FILE_SIZE)
            {
                Size = -1;
            }
            else if (_upperWord > 0)
            {
                Size = (((long)_upperWord) << 32) + Size;
            }
        }
    
        public long Size { get; }
    
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
    
        protected virtual void Dispose(bool disposing)
        {
            if (_disposed)
            {
                return;
            }
    
            // Dispose of managed resources here.
            if (disposing)
            {
                _safeHandle?.Dispose();
            }
    
            // Dispose of any unmanaged resources not wrapped in safe handles.
    
            _disposed = true;
        }
    }

    DisposeAsync() 

     无参数的 DisposeAsync() 方法在 await using 语句中隐式调用,它具有标准实现:

    public async ValueTask DisposeAsync()
    {
        // Perform async cleanup.
        await DisposeAsyncCore();
    
        // Dispose of managed resources.
        Dispose(false);
        // Suppress finalization.
        GC.SuppressFinalize(this);
    }

    异步释放模式的主要差异在于,从 DisposeAsync() 到 Dispose(bool) 重载方法的调用被赋予 false 作为参数。 但实现 IDisposable.Dispose() 方法时,改为传递 true。 换句话说,DisposeAsyncCore() 方法将异步释放托管资源,因此不希望也同步释放这些资源。 因此,调用 Dispose(false) 而非 Dispose(true)

    using System;
    using System.Text.Json;
    using System.Threading.Tasks;
    
    public class ExampleAsyncDisposable : IAsyncDisposable, IDisposable
    {
        // To detect redundant calls
        private bool _disposed = false;
    
        // Created in .ctor, omitted for brevity.
        private Utf8JsonWriter _jsonWriter;
    
        public async ValueTask DisposeAsync()
        {
            await DisposeAsyncCore();
    
            Dispose(false);
            GC.SuppressFinalize(this);
        }
    
        protected virtual async ValueTask DisposeAsyncCore()
        {
            // Cascade async dispose calls
            if (_jsonWriter != null)
            {
                await _jsonWriter.DisposeAsync();
                _jsonWriter = null;
            }
        }
    
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
    
        protected virtual void Dispose(bool disposing)
        {
            if (_disposed)
            {
                return;
            }
    
            if (disposing)
            {
                _jsonWriter?.Dispose();
                // TODO: dispose managed state (managed objects).
            }
    
            // TODO: free unmanaged resources (unmanaged objects) and override a finalizer below.
            // TODO: set large fields to null.
    
            _disposed = true;
        }
    }

    堆叠的 using

    在创建和使用实现 IAsyncDisposable 的多个对象的情况下,残存错误条件中堆叠的 using 语句可能会阻止调用 DisposeAsync()。 为了帮助防止潜在的问题,应避免堆叠,并遵循以下示例模式:

    class ExampleProgram
    {
        static async Task Main()
        {
            var objOne = new ExampleAsyncDisposable();
            await using objOne.ConfigureAwait(false);
            // Interact with the objOne instance.
    
            var objTwo = new ExampleAsyncDisposable();
            await using objTwo.ConfigureAwait(false))
            {
                // Interact with the objTwo instance.
            }
    
            Console.ReadLine();
        }
    }

    使用 C# using 语句,还可以在一个语句(在内部相当于嵌套语句 using)中获取多个资源。 下面的示例实例化两个 StreamReader 对象以读取两个不同文件的内容。

    using StreamReader version1 = new StreamReader("file1.txt"),
                               version2 = new StreamReader("file2.txt");
  • 相关阅读:
    angularJS之路由
    angularJS之ng-repeat
    智能算法之Matlab实现(1)——遗传算法(1)
    如何快速处理线上故障
    测试计划怎么写
    接口测试基础
    HTTP 的一些问题
    DevOps简介
    什么是DevOps?
    HTTPS 如何保证数据传输的安全性
  • 原文地址:https://www.cnblogs.com/yetsen/p/13497447.html
Copyright © 2011-2022 走看看