>  기사  >  백엔드 개발  >  .Net의 IDisposable 인터페이스

.Net의 IDisposable 인터페이스

高洛峰
高洛峰원래의
2016-11-21 14:54:561442검색

.Net Framework의 가비지 수집은 프로그래머가 관리되는 리소스를 자동으로 재활용하는 데 도움이 됩니다. 이는 클래스 라이브러리 호출자에게 매우 즐거운 경험입니다. 모든 객체는 언제 어디서나 생성될 수 있으며 GC는 이를 수행합니다. 항상 진실을 말할 것입니다. 우리가 클래스 라이브러리 제공자인 경우 어떻게 그렇게 좋은 경험을 제공할 수 있습니까?

먼저 .Net 프레임워크에서 관리되는 리소스는 무엇이고, 관리되지 않는 리소스는 무엇인가요?

기본적으로 .Net 프레임워크의 모든 클래스는 다양한 스트림(예: FileStream, MemoryStream), 데이터베이스 연결, 구성 요소 등을 포함하여 관리되는 리소스입니다. . 파일을 차지하는 다른 미사용 메소드를 작성하세요. FileStream은 단지 로컬 변수입니다. 이 메소드가 반환되면 재활용되어야 합니다.

마지막으로 필수 대기 시간이 있습니다.

        private static void MonitorFileStatus(string fileName)
        {
            Console.WriteLine("Start to monitor file: {0}", fileName);
            Task.Factory.StartNew(() =>
            {
                while(true)
                {
                    bool isInUse = IsFileInUse(fileName);

                    string messageFormat = isInUse ? "File {0} is in use." : "File {0} is released.";
                    Console.WriteLine(messageFormat, fileName);
                    Thread.Sleep(oneSeconds);
                }
            });
        }


        private static bool IsFileInUse(string fileName)
        {
            bool isInUse = true;
            FileStream stream = null;
            try
            {
                stream = File.Open(fileName, FileMode.Append, FileAccess.Write);
                isInUse = false;
            }
            catch
            {
            }
            finally
            {
                if (stream != null)
                {
                    stream.Dispose();
                }
            }
            return isInUse;
        }

조합은 테스트입니다.

먼저 파일 모니터링 스레드를 시작한 다음 사용하지 않고 파일을 엽니다.
        private static void OpenFile()
        {
            FileStream stream  = File.Open(TestFileName, FileMode.Append, FileAccess.Write);
            Wait(fiveSeconds);
        }
OpenFile 메서드가 반환되어 FileStream이 재활용될 것이라고 예측합니다.

그런 다음 GC를 호출하여 파일이 릴리스되었는지 확인합니다.

        private static void Wait(TimeSpan time)
        {
            Console.WriteLine("Wait for {0} seconds...", time.TotalSeconds);
            Thread.Sleep(time);
        }

실행 결과를 보면 GC가 자동으로 FileStream을 재활용하는 것으로 나타났습니다. . Dispose 메소드를 호출할 필요도 없고, using


        private static void FileTest()
        {
            MonitorFileStatus(TestFileName);

            OpenFile();

            CallGC();
            Wait(fiveSeconds);
        }
그럼 관리되지 않는 리소스에는 무엇이 포함되나요?

일반적으로 Windows API pinvoke의 경우 다양한 intptr이 관리되지 않는 리소스입니다. 예를 들어 다음과 같이 파일을 열면 관리되지 않는 리소스가 포함됩니다.

.Net의 IDisposable 인터페이스

관리되지 않는 리소스를 처리하려면 IDisposable 인터페이스를 구현해야 합니다. 두 가지 이유가 있습니다.

은 소멸자에 의존할 수 없습니다. 왜냐하면 이종 함수의 호출은 GC에 의해 결정되기 때문입니다. 부족한 자원은 실시간으로 풀릴 수 없습니다.

일반적인 처리 원칙이 있습니다. 소멸자는 관리되는 리소스를 처리하고 IDisposable 인터페이스는 관리되는 리소스와 관리되지 않는 리소스를 처리합니다.
    [Flags]
    internal enum OpenFileStyle : uint
    {
        OF_CANCEL = 0x00000800,  // Ignored. For a dialog box with a Cancel button, use OF_PROMPT.
        OF_CREATE = 0x00001000,  // Creates a new file. If file exists, it is truncated to zero (0) length.
        OF_DELETE = 0x00000200,  // Deletes a file.
        OF_EXIST = 0x00004000,  // Opens a file and then closes it. Used to test that a file exists
        OF_PARSE = 0x00000100,  // Fills the OFSTRUCT structure, but does not do anything else.
        OF_PROMPT = 0x00002000,  // Displays a dialog box if a requested file does not exist 
        OF_READ = 0x00000000,  // Opens a file for reading only.
        OF_READWRITE = 0x00000002,  // Opens a file with read/write permissions.
        OF_REOPEN = 0x00008000,  // Opens a file by using information in the reopen buffer.

        // For MS-DOS–based file systems, opens a file with compatibility mode, allows any process on a 
        // specified computer to open the file any number of times.
        // Other efforts to open a file with other sharing modes fail. This flag is mapped to the 
        // FILE_SHARE_READ|FILE_SHARE_WRITE flags of the CreateFile function.
        OF_SHARE_COMPAT = 0x00000000,

        // Opens a file without denying read or write access to other processes.
        // On MS-DOS-based file systems, if the file has been opened in compatibility mode
        // by any other process, the function fails.
        // This flag is mapped to the FILE_SHARE_READ|FILE_SHARE_WRITE flags of the CreateFile function.
        OF_SHARE_DENY_NONE = 0x00000040,

        // Opens a file and denies read access to other processes.
        // On MS-DOS-based file systems, if the file has been opened in compatibility mode,
        // or for read access by any other process, the function fails.
        // This flag is mapped to the FILE_SHARE_WRITE flag of the CreateFile function.
        OF_SHARE_DENY_READ = 0x00000030,

        // Opens a file and denies write access to other processes.
        // On MS-DOS-based file systems, if a file has been opened in compatibility mode,
        // or for write access by any other process, the function fails.
        // This flag is mapped to the FILE_SHARE_READ flag of the CreateFile function.
        OF_SHARE_DENY_WRITE = 0x00000020,

        // Opens a file with exclusive mode, and denies both read/write access to other processes.
        // If a file has been opened in any other mode for read/write access, even by the current process,
        // the function fails.
        OF_SHARE_EXCLUSIVE = 0x00000010,

        // Verifies that the date and time of a file are the same as when it was opened previously.
        // This is useful as an extra check for read-only files.
        OF_VERIFY = 0x00000400,

        // Opens a file for write access only.
        OF_WRITE = 0x00000001
    }

    [StructLayout(LayoutKind.Sequential)]
    internal struct OFSTRUCT
    {
        public byte cBytes;
        public byte fFixedDisc;
        public UInt16 nErrCode;
        public UInt16 Reserved1;
        public UInt16 Reserved2;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
        public string szPathName;
    }

    class WindowsApi
    {
        [DllImport("kernel32.dll", BestFitMapping = false, ThrowOnUnmappableChar = true)]
        internal static extern IntPtr OpenFile([MarshalAs(UnmanagedType.LPStr)]string lpFileName, out OFSTRUCT lpReOpenBuff, OpenFileStyle uStyle);

        [DllImport("kernel32.dll", SetLastError = true)]
        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
        [SuppressUnmanagedCodeSecurity]
        [return: MarshalAs(UnmanagedType.Bool)]
        internal static extern bool CloseHandle(IntPtr hObject);
    }

위 예시와 같이 완성된 구현 코드는 다음과 같습니다.

같은 클래스에 관리되는 리소스와 관리되지 않는 리소스가 모두 있는 경우 어떻게 해야 할까요?

은 다음 패턴을 따를 수 있습니다.

    public class UnmanagedFileHolder : IFileHolder, IDisposable
    {
        private IntPtr _handle;
        private string _fileName;

        public UnmanagedFileHolder(string fileName)
        {
            _fileName = fileName;
        }

        public void OpenFile()
        {
            Console.WriteLine("Open file with windows api.");
            OFSTRUCT info;
            _handle = WindowsApi.OpenFile(_fileName, out info, OpenFileStyle.OF_READWRITE);
        }

        #region IDisposable Support
        private bool disposed = false;

        protected virtual void Dispose(bool disposing)
        {
            if (!disposed)
            {
                if (disposing)
                {
                    // no managed resource
                }
                WindowsApi.CloseHandle(_handle);
                _handle = IntPtr.Zero;

                disposed = true;
            }
        }

        ~UnmanagedFileHolder()
        {
            Dispose(false);
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
        #endregion
    }
다음은 관리되는 FileStream 및 관리되지 않는 Handler를 포함하는 완전한 예입니다

마지막으로 클래스는 어떻습니까? IDisposable 인터페이스를 구현하지 않습니까? 예를 들어 byte[], StringBuilder

    class HybridPattern : IDisposable
    {
        private bool _disposed = false;

        ~HybridPattern()
        {
            Dispose(false);
        }

        protected void Dispose(bool disposing)
        {
            if (_disposed)
            {
                return;
            }

            if (disposing)
            {
                // Code to dispose the managed resources of the class
                // internalComponent1.Dispose();
            }

            // Code to dispose the un-managed resources of the class
            // CloseHandle(handle);
            // handle = IntPtr.Zero;

            _disposed = true;
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
    }
는 재활용을 전혀 방해하지 않으며 GC는 잘 작동합니다.

소멸자에서 거대한 byte[]를 null로 설정하려고 시도했지만 유일한 결과는 다음 GC 주기까지 수집이 지연된다는 것이었습니다.

이유도 매우 간단합니다. 참조가 이루어질 때마다 해당 참조 트리의 개수가 1씩 증가합니다. .
    public class HybridHolder : IFileHolder, IDisposable
    {
        private string _unmanagedFile;
        private string _managedFile;

        private IntPtr _handle;
        private FileStream _stream;

        public HybridHolder(string unmanagedFile, string managedFile)
        {
            _unmanagedFile = unmanagedFile;
            _managedFile = managedFile;
        }

        public void OpenFile()
        {
            Console.WriteLine("Open file with windows api.");
            OFSTRUCT info;
            _handle = WindowsApi.OpenFile(_unmanagedFile, out info, OpenFileStyle.OF_READWRITE);

            Console.WriteLine("Open file with .Net libray.");
            _stream = File.Open(_managedFile, FileMode.Append, FileAccess.Write);
        }

        #region IDisposable Support
        private bool disposed = false;

        protected virtual void Dispose(bool disposing)
        {
            if (!disposed)
            {
                //Console.WriteLine("string is null? {0}", _stream == null);
                if (disposing && _stream != null)
                {
                    Console.WriteLine("Clean up managed resource.");
                    _stream.Dispose();
                }

                Console.WriteLine("Clean up unmanaged resource.");
                WindowsApi.CloseHandle(_handle);
                _handle = IntPtr.Zero;

                disposed = true;
            }
        }

        ~HybridHolder()
        {
            Dispose(false);
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
        #endregion
    }

전체 코드는 Github에서 찾을 수 있습니다:


https://github.com/IGabriel/IDisposableSample

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.