programing

ID 일회용 인터페이스의 올바른 사용

jooyons 2023. 5. 10. 20:50
반응형

ID 일회용 인터페이스의 올바른 사용

Microsoft 설명서를 읽어본 결과, "기본" 사용법이IDisposable인터페이스는 관리되지 않는 리소스를 정리하는 것입니다.

되지 않음"은 데이터베이스 합니다."관리되지 않음"은 데이터베이스 연결, 소켓, 창 핸들 등을 의미합니다. 저는 만코본적있어요이를드지하가 있는 를 본 이 있습니다.Dispose()관리되는 리소스를 해방하기 위해 메소드가 구현되는데, 이는 가비지 수집가가 이를 대신 처리해야 하기 때문에 저에게는 중복된 것으로 보입니다.

예:

public class MyCollection : IDisposable
{
    private List<String> _theList = new List<String>();
    private Dictionary<String, Point> _theDict = new Dictionary<String, Point>();

    // Die, clear it up! (free unmanaged resources)
    public void Dispose()
    {
        _theList.clear();
        _theDict.clear();
        _theList = null;
        _theDict = null;
    }
}

를 빈 메모리로 .MyCollection평소보다 더 빠른가요?


편집: 지금까지 사람들은 사용의 좋은 예를 게시했습니다.IDisposable데이터베이스 연결 및 비트맵과 같은 관리되지 않는 리소스를 정리합니다.하지만 그렇다고 가정해 보세요._theList위의 코드에는 백만 개의 문자열이 포함되어 있으며, 당신은 가비지 컬렉터를 기다리는 대신 지금 그 메모리를 해제하기를 원했습니다.위의 코드가 그것을 달성할 수 있습니까?

폐기는 관리되지 않는 리소스를 해제하는 것입니다.어느 시점에 완료해야 합니다. 그렇지 않으면 절대 청소되지 않을 것입니다.쓰레기 수집가가 전화하는 방법을 모릅니다.DeleteHandle() 에.IntPtr전화를 해야 할지 말아야 할지 알 수 없습니다.DeleteHandle().

참고: 관리되지 않는 리소스란 무엇입니까?마이크로소프트에서 찾으셨다면요.NET Framework: 관리됩니다.당신이 직접 MSDN을 찾아다녔다면 관리되지 않는 것입니다.P/Invoke 통화를 사용하여 에서 사용할 수 있는 모든 것의 편안한 세계를 벗어나려는 경우.NET Framework는 관리되지 않으며 이제 사용자가 정리해야 합니다.

생성한 개체는 관리되지 않는 리소스를 정리하기 위해 외부에서 호출할 수 있는 메서드를 노출해야 합니다.메소드의 이름은 원하는 대로 지정할 수 있습니다.

public void Cleanup()

또는

public void Shutdown()

대신 이 방법에 대한 표준화된 이름이 있습니다.

public void Dispose()

인 인이스가만했지만도기들지어심터지,IDisposable한방법을 있습니다: 한가지방법있이습니다단있:다단습▁that.

public interface IDisposable
{
   void Dispose();
}

의 사물을 시키는 것입니다.IDisposable관리되지 않는 리소스를 정리하기 위한 단일 방법을 작성했음을 보장합니다.

public void Dispose()
{
   Win32.DestroyHandle(this.CursorFileBitmapIconServiceHandle);
}

그리고 당신은 끝났습니다.당신이 더 잘할 수 있다는 것만 빼면요.


개체가 250MB 시스템을 할당한 경우에는 어떻게 해야 합니까?그림 그리기.비트맵(즉, .일종의 프레임 버퍼로 NET 관리 비트맵 클래스)?물론입니다. 이것은 관리됩니다.NET 개체이며 가비지 수집기가 이 개체를 해제합니다.하지만 250MB의 메모리를 그냥 두고 쓰레기 수집기가 와서 분리되기를 기다리시겠습니까?데이터베이스 연결이 열려 있으면 어떻게 합니까?확실히 우리는 GC가 객체를 완성하기를 기다리며 연결이 열려 있는 것을 원하지 않습니다.

를 걸었을 경우Dispose()(더 이상 객체를 사용할 계획이 없다는 뜻) 이러한 낭비적인 비트맵과 데이터베이스 연결을 제거하는 것은 어떻습니까?

이제 다음을 수행합니다.

  • 관리되지 않는 리소스 제거(필요에 따라)
  • 관리되는 리소스 제거(도움이 되고 싶기 때문)

업데이트를 해보겠습니다.Dispose()이러한 관리 개체를 제거하는 방법:

public void Dispose()
{
   //Free unmanaged resources
   Win32.DestroyHandle(this.CursorFileBitmapIconServiceHandle);

   //Free managed resources too
   if (this.databaseConnection != null)
   {
      this.databaseConnection.Dispose();
      this.databaseConnection = null;
   }
   if (this.frameBufferImage != null)
   {
      this.frameBufferImage.Dispose();
      this.frameBufferImage = null;
   }
}

그리고 당신이 더 잘할 수 있다는 것을 제외하고는 모든 것이 좋습니다!


만약 그 사람이 전화하는 것을 잊었다면요?Dispose()당신의 목적에?그러면 그들은 관리되지 않는 자원들을 유출할 것입니다!

참고: 가비지 수집기가 백그라운드 스레드에서 실행되고 사용되지 않는 개체와 연결된 메모리가 확보되기 때문에 관리되는 리소스가 유출되지 않습니다.여기에는 사용자의 개체 및 사용하는 모든 관리 개체)가 포함됩니다.Bitmap 리고그고.DbConnection).

그 사람이 전화하는 것을 잊어버린 경우Dispose()우리는 여전히 그들의 베이컨을 구할 수 있습니다!우리는 여전히 그것을 부를 방법이 있습니다: 쓰레기 수집가가 마침내 우리의 목적을 자유롭게 할 수 있을 때(즉, 최종화).

참고: 가비지 수집기는 결국 모든 관리 개체를 해제합니다.그러면 개체에 대한 메서드를 호출합니다.GC는 처분 방법에 대해 알지 못하거나 관심이 없습니다.그것은 우리가 관리되지 않는 것들을 없애고 싶을 때 부르는 방법을 위해 선택한 이름일 뿐입니다.

가비지 수집기에 의한 개체 파괴는 성가신 관리되지 않는 리소스를 해방하기에 완벽한 시기입니다.우리는 이것을 우선시함으로써 합니다.Finalize()방법.

참고: C#에서는 다음을 명시적으로 재정의하지 않습니다.Finalize()방법.당신은 C++ 소멸기처럼 보이는 방법을 쓰고, 컴파일러는 그것을 당신의 구현으로 받아들입니다.Finalize()방법:

~MyObject()
{
    //we're being finalized (i.e. destroyed), call Dispose in case the user forgot to
    Dispose(); //<--Warning: subtle bug! Keep reading!
}

하지만 그 코드에는 버그가 있습니다.쓰레기 수집기는 백그라운드 스레드에서 실행됩니다. 두 개체가 파괴되는 순서를 알 수 없습니다.당신 안에서 그것은 전적으로 가능합니다.Dispose()(도움이 되고 싶어서) 제거하려는 관리 개체인 코드가 더 이상 없습니다.

public void Dispose()
{
   //Free unmanaged resources
   Win32.DestroyHandle(this.gdiCursorBitmapStreamFileHandle);

   //Free managed resources too
   if (this.databaseConnection != null)
   {
      this.databaseConnection.Dispose(); //<-- crash, GC already destroyed it
      this.databaseConnection = null;
   }
   if (this.frameBufferImage != null)
   {
      this.frameBufferImage.Dispose(); //<-- crash, GC already destroyed it
      this.frameBufferImage = null;
   }
}

그래서 당신에게 필요한 것은Finalize()에게 말하다Dispose()관리되지 않는 리소스를 해제하는 동시에 관리되는 리소스를 건드리지 않아야 합니다.

이를 위한 표준 패턴은 다음과 같습니다.Finalize()그리고.Dispose()둘 다 세 번째(!) 메서드를 호출합니다. 여기서 호출할 경우 부울 구문을 전달합니다.Dispose())와 반대로Finalize()즉되는 리소스를

내부 메서드는 "CoreDisposure" 또는 "MyInternalDisposure"와 같은 임의의 이름을 가질 수 있지만, 이를 부르는 것이 전통입니다.Dispose(Boolean):

protected void Dispose(Boolean disposing)

그러나 더 유용한 매개 변수 이름은 다음과 같습니다.

protected void Dispose(Boolean itIsSafeToAlsoFreeManagedObjects)
{
   //Free unmanaged resources
   Win32.DestroyHandle(this.CursorFileBitmapIconServiceHandle);

   //Free managed resources too, but only if I'm being called from Dispose
   //(If I'm being called from Finalize then the objects might not exist
   //anymore
   if (itIsSafeToAlsoFreeManagedObjects)  
   {    
      if (this.databaseConnection != null)
      {
         this.databaseConnection.Dispose();
         this.databaseConnection = null;
      }
      if (this.frameBufferImage != null)
      {
         this.frameBufferImage.Dispose();
         this.frameBufferImage = null;
      }
   }
}

그고당신당구변경다니합현을신의은리ation▁of▁the▁your다▁implement▁and▁change니의 구현을 변경합니다.IDisposable.Dispose()방법:

public void Dispose()
{
   Dispose(true); //I am calling you from Dispose, it's safe
}

그리고 최종 결정자는 다음을 수행합니다.

~MyObject()
{
   Dispose(false); //I am *not* calling you from Dispose, it's *not* safe
}

참고: 객체가 구현되는 객체에서 하강하는 경우Dispose그런 다음 Dispose(폐기)를 재정의할 때는 기본 방법인 Dispose(폐기)를 호출하는 것을 잊지 마십시오.

public override void Dispose()
{
    try
    {
        Dispose(true); //true: safe to free managed resources
    }
    finally
    {
        base.Dispose();
    }
}

그리고 당신이 더 잘할 수 있다는 것을 제외하고는 모든 것이 좋습니다!


사용자가 전화하는 경우Dispose()당신의 물건에, 그러면 모든 것이 정리되었습니다.Finalize를 Finish라고 부를 입니다.Dispose한 번

이것은 낭비일 뿐만 아니라, 만약 당신의 객체가 당신이 마지막 통화로부터 이미 폐기한 객체에 대한 정크 참조를 가지고 있다면.Dispose()당신은 그것들을 다시 처분하려고 할 것입니다!

내 코드에서 내가 삭제한 개체에 대한 참조를 제거하는 데 주의했다는 것을 알게 될 것입니다. 그래서 나는 전화를 걸려고 하지 않습니다.Dispose정크 오브제 참조.하지만 그것은 미묘한 벌레가 슬금슬금 들어오는 것을 막지는 못했습니다.

가 사자가전때할화용을 부를 때.Dispose()CursorFileBitmapIconServiceHandle 핸들이 삭제되었습니다.나중에 가비지 수집기가 실행될 때 동일한 핸들을 다시 파괴하려고 시도합니다.

protected void Dispose(Boolean iAmBeingCalledFromDisposeAndNotFinalize)
{
   //Free unmanaged resources
   Win32.DestroyHandle(this.CursorFileBitmapIconServiceHandle); //<--double destroy 
   ...
}

이 문제를 해결하는 방법은 가비지 수집기에 개체를 최종화할 필요가 없음을 알려주는 것입니다. 가비지 수집기의 리소스는 이미 정리되었으므로 더 이상 작업이 필요하지 않습니다.전화를 통해 이 작업을 수행합니다.GC.SuppressFinalize()에 시대에Dispose()방법:

public void Dispose()
{
   Dispose(true); //I am calling you from Dispose, it's safe
   GC.SuppressFinalize(this); //Hey, GC: don't bother calling finalize later
}

가 사자가전했로로 전화를 걸었습니다.Dispose()다음이 있습니다.

  • 해제된 관리되지 않는 리소스
  • 사용 가능한 관리 리소스

GC가 파이널라이저를 실행하는 것은 의미가 없습니다. 모든 것이 해결되었습니다.

Finalize를 사용하여 관리되지 않는 리소스를 정리할 수 없습니까?

의 설명서는 다음과 같습니다.

완료 메서드는 개체가 삭제되기 전에 현재 개체가 보유한 관리되지 않는 리소스에 대해 정리 작업을 수행하는 데 사용됩니다.

그러나 MSDN 문서에는 다음과 같은 내용도 나와 있습니다.

관리되지 않는 리소스의 해제, 해제 또는 재설정과 관련된 응용 프로그램 정의 작업을 수행합니다.

그래서 어느 쪽인가요?관리되지 않는 리소스를 정리할 수 있는 위치는 어디입니까?답은 다음과 같습니다.

그건 당신의 선택입니다!하지만 선택하세요.Dispose.

관리되지 않는 정리를 피니시저에 배치할 수 있습니다.

~MyObject()
{
   //Free unmanaged resources
   Win32.DestroyHandle(this.CursorFileBitmapIconServiceHandle);

   //A C# destructor automatically calls the destructor of its base class.
}

문제는 쓰레기 수집가들이 언제 당신의 물건을 완성할 수 있을지 모른다는 것입니다.관리되지 않고, 필요하지 않으며, 사용되지 않는 기본 리소스는 가비지 수집기가 실행될 때까지 계속 유지됩니다.그런 다음 관리되지 않는 리소스를 정리하는 최종화 방법을 호출합니다.개체에 대한 설명서입니다. 점을 마무리합니다.

피니시저가 실행되는 정확한 시간은 정의되지 않았습니다.클래스 인스턴스에 대한 리소스의 결정론적 릴리스를 보장하려면 Close 메서드를 구현하거나 구현을 제공합니다.

이것이 사용의 장점입니다.Dispose관리되지 않는 리소스를 정리합니다. 관리되지 않는 리소스가 정리되는 시기를 파악하고 제어할 수 있습니다.그들의 파괴는 "결정론적"입니다.


원래 질문에 답하기GC가 메모리를 해제하기로 결정했을 때보다 지금 메모리를 해제하는 것이 어떻습니까?저는 얼굴 인식 소프트웨어를 가지고 있어서 530MB의 내부 이미지를 제거해야 합니다. 더 이상 필요하지 않기 때문입니다.그렇지 않을 때: 기계가 갈려서 교환이 중단됩니다.

보너스 판독치

이 대답의 스타일을 좋아하는 사람이라면 (이유를 설명하고, 그래서 어떻게 하면 분명해질까를) 돈 박스의 필수 COM의 1장을 읽어보길 제안합니다.

그는 35페이지에서 이진 객체 사용의 문제점을 설명하고, 당신의 눈앞에서 COM을 발명합니다.COM의 이유를 알게 되면 나머지 300페이지는 분명하며 마이크로소프트의 구현에 대해 자세히 설명합니다.

저는 객체나 COM을 다뤄본 모든 프로그래머들이 적어도 첫 장을 읽어야 한다고 생각합니다.그것은 그 어떤 것에 대한 최고의 설명입니다.

추가 보너스 판독치

당신이 아는 모든 것이 에릭 리퍼트에 의해 잘못되었을 때.

따라서 정확한 피니시저를 작성하는 것은 매우 어려운 이며, 제가 드릴 있는 최선의 조언은 시도하지 말라는 것입니다.

IDisposable를 악용하는 데 자주 사용됩니다.using관리 개체를 결정적으로 정리할 수 있는 쉬운 방법을 사용할 수 있습니다.

public class LoggingContext : IDisposable {
    public Finicky(string name) {
        Log.Write("Entering Log Context {0}", name);
        Log.Indent();
    }
    public void Dispose() {
        Log.Outdent();
    }

    public static void Main() {
        Log.Write("Some initial stuff.");
        try {
            using(new LoggingContext()) {
                Log.Write("Some stuff inside the context.");
                throw new Exception();
            }
        } catch {
            Log.Write("Man, that was a heavy exception caught from inside a child logging context!");
        } finally {
            Log.Write("Some final stuff.");
        }
    }
}

폐기 패턴의 목적은 관리되는 리소스와 관리되지 않는 리소스를 모두 정리하는 메커니즘을 제공하는 것이며 이러한 메커니즘은 폐기 메서드가 호출되는 방식에 따라 달라집니다.이 예제에서는 목록을 지우면 해당 컬렉션이 삭제되는 데 영향을 미치지 않으므로 폐기와 관련된 작업은 실제로 수행되지 않습니다.마찬가지로 변수를 null로 설정하는 호출도 GC에 영향을 주지 않습니다.

문서에서 폐기 패턴을 구현하는 방법에 대한 자세한 내용을 볼 수 있지만 기본적으로 다음과 같습니다.

public class SimpleCleanup : IDisposable
{
    // some fields that require cleanup
    private SafeHandle handle;
    private bool disposed = false; // to detect redundant calls

    public SimpleCleanup()
    {
        this.handle = /*...*/;
    }

    protected virtual void Dispose(bool disposing)
    {
        if (!disposed)
        {
            if (disposing)
            {
                // Dispose managed resources.
                if (handle != null)
                {
                    handle.Dispose();
                }
            }

            // Dispose unmanaged managed resources.

            disposed = true;
        }
    }

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

여기서 가장 중요한 방법은 폐기(bool)이며, 실제로는 두 가지 다른 상황에서 실행됩니다.

  • 처분 == true: 메서드가 사용자 코드에 의해 직접 또는 간접적으로 호출되었습니다.관리되는 리소스와 관리되지 않는 리소스를 삭제할 수 있습니다.
  • 배치 == false: 메서드는 피니시저 내부에서 런타임에 의해 호출되었으므로 다른 개체를 참조하면 안 됩니다.관리되지 않는 리소스만 삭제할 수 있습니다.

GC가 정리를 수행하도록 하는 것의 문제는 GC가 수집 주기를 실행할 시기를 실질적으로 제어할 수 없다는 것입니다(GC를 호출할 수 있음).리소스가 필요 이상으로 오래 유지될 수 있도록 수집(하지만 실제로는 그렇게 해서는 안 됩니다).Dispose()를 호출하면 실제로 수집 주기가 발생하거나 어떤 방식으로든 GC가 개체를 수집/해제하지 않습니다. 사용된 리소스를 보다 확실하게 정리하고 GC에 이 정리가 이미 수행되었음을 알려주는 수단을 제공할 뿐입니다.

ID의 전체 요점은 처분 가능하며 처분 패턴은 메모리를 즉시 확보하는 것이 아닙니다.Dispose 호출이 실제로 메모리를 즉시 확보할 수 있는 유일한 경우는 Disposing == false 시나리오를 처리하고 관리되지 않는 리소스를 조작하는 경우입니다.관리되는 코드의 경우 GC가 수집 주기를 실행할 때까지 메모리가 실제로 회수되지 않습니다. GC를 호출하는 것 외에는 이를 제어할 수 없습니다.이미 언급한 Collect()는 좋은 생각이 아닙니다.

에서 문자열을 입력했기 때문에 시나리오가 유효하지 않습니다.NET은 변경되지 않은 리소스를 사용하지 않고 ID 폐기를 구현하지 않습니다. 이러한 리소스를 "정리"하도록 강제할 방법이 없습니다.

Dispose가 호출된 후에는 개체 메서드를 더 이상 호출하지 않아야 합니다(개체는 Dispose에 대한 추가 호출을 허용해야 함).그러므로 질문의 예는 어리석습니다.Dispose가 호출되면 개체 자체를 삭제할 수 있습니다.따라서 사용자는 해당 전체 개체에 대한 모든 참조를 삭제(null로 설정)하면 내부의 모든 관련 개체가 자동으로 정리됩니다.

관리됨/관리되지 않음에 대한 일반적인 질문과 다른 답변에 대한 논의는 관리되지 않는 리소스에 대한 정의로 시작해야 한다고 생각합니다.

요약하자면, 시스템을 상태로 만들기 위해 호출할 수 있는 기능이 있고, 그 상태에서 다시 호출할 수 있는 다른 기능이 있습니다.예에서 첫핸들을 이고, 두일 수 .CloseHandle.

하지만 - 그리고 이것이 핵심입니다 - 그들은 일치하는 함수의 쌍이 될 수 있습니다.하나는 국가를 건설하고, 다른 하나는 국가를 파괴합니다.상태가 구축되었지만 아직 해체되지 않은 경우 리소스 인스턴스가 존재합니다.리소스가 CLR에 의해 관리되지 않는 적절한 시기에 해체되도록 준비해야 합니다.자동으로 관리되는 유일한 리소스 유형은 메모리입니다.GC와 스택의 두 가지 종류가 있습니다.값 유형은 스택에 의해 관리되고(또는 참조 유형 내부의 승차를 히치하여) 참조 유형은 GC에 의해 관리됩니다.

이러한 함수는 자유롭게 인터리빙할 수 있거나 완벽하게 중첩되어야 하는 상태 변화를 일으킬 수 있습니다.상태 변경은 스레드 세이프일 수도 있고 그렇지 않을 수도 있습니다.

Justice의 질문에 있는 예를 보세요.로그 파일 들여쓰기의 변경 내용은 완전히 중첩되어야 합니다. 그렇지 않으면 모든 것이 잘못됩니다.또한 그들은 스레드 세이프일 가능성이 낮습니다.

관리되지 않는 리소스를 정리하기 위해 가비지 수집기에 편승할 수 있습니다.그러나 상태 변경 기능이 스레드 세이프이고 두 상태가 어떤 방식으로든 겹치는 수명을 가질 수 있는 경우에만 가능합니다.따라서 Justice의 자원 예에는 파이널라이저가 없어야 합니다!아무에게도 도움이 되지 않을 뿐입니다.

이러한 종류의 리소스에 대해서는 다음을 구현할 수 있습니다.IDisposable최종 결정자 없이피니시저는 절대적으로 선택 사항이며, 선택 사항이어야 합니다.이것은 많은 책에서 얼버무리거나 언급조차 되지 않습니다.

그러면 다음을 사용해야 합니다.using다음을 보장할 가능성이 있는 진술.Dispose이 호출됩니다.을 하는 것과 에 대한 이므로, 이은본질에따것같습니다과는치하히차. (를라파라이는저됩연 GC다니결널서이으로적).)using스택에 대한 것입니다.

누락된 부분은 Dispose를 수동으로 작성하여 필드와 기본 클래스에 호출해야 한다는 것입니다.C++/CLI 프로그래머는 그렇게 할 필요가 없습니다.대부분의 경우 컴파일러가 이를 대신 작성합니다.

완벽하게 둥지를 틀고 스레드 세이프가 아닌 주를 위해 선호하는 대안이 있습니다(다른 것과는 별개로, ID 일회용을 피하는 것은 ID 일회용을 구현하는 모든 클래스에 파이널라이저를 추가하는 것을 거부할 수 없는 누군가와 논쟁하는 문제를 덜어줍니다).

수업을 쓰는 대신 함수를 씁니다.이 함수는 다음으로 다시 호출할 대리자를 수락합니다.

public static void Indented(this Log log, Action action)
{
    log.Indent();
    try
    {
        action();
    }
    finally
    {
        log.Outdent();
    }
}

그리고 간단한 예는 다음과 같습니다.

Log.Write("Message at the top");
Log.Indented(() =>
{
    Log.Write("And this is indented");

    Log.Indented(() =>
    {
        Log.Write("This is even more indented");
    });
});
Log.Write("Back at the outermost level again");

전달되는 람다는 코드 블록 역할을 하므로, 다음과 같은 목적을 수행하기 위해 자신만의 제어 구조를 만드는 것과 같습니다.using호출자가 더 이상 그것을 남용할 위험이 없다는 것을 제외하고는.그들이 자원을 정리하지 못할 리가 없습니다.

리소스 수명이 중복될 수 있는 유형인 경우 이 기술은 리소스 A, 리소스 B를 빌드한 다음 리소스 A를 종료하고 나중에 리소스 B를 종료할 수 있기 때문에 유용하지 않습니다.이렇게 완벽하게 둥지를 틀도록 사용자에게 강요했다면 그렇게 할 수 없습니다.하지만 그럼 당신은 사용해야 합니다.IDisposable(그러나 무료가 아닌 스레드 안전을 구현하지 않은 경우에는 파이널라이저가 없습니다.)

ID를 사용하지 않는 시나리오: 관리되지 않는 리소스 정리, 이벤트 등록 취소, 연결 닫기

ID 구현을 위해 사용하는 관용구(스레드세이프 아님):

class MyClass : IDisposable {
    // ...

    #region IDisposable Members and Helpers
    private bool disposed = false;

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

    private void Dispose(bool disposing) {
        if (!this.disposed) {
            if (disposing) {
                // cleanup code goes here
            }
            disposed = true;
        }
    }

    ~MyClass() {
        Dispose(false);
    }
    #endregion
}

이않는 작업(즉, 하지 않음)을 수행하지 그 코 완 는 전 중 즉 히 않 MyCollection 인 않 히나특..Clear()전화가 걸려오는 전화.

편집 내용에 대한 답변:약간.이 작업을 수행할 경우:

public void WasteMemory()
{
    var instance = new MyCollection(); // this one has no Dispose() method
    instance.FillItWithAMillionStrings();
}

// 1 million strings are in memory, but marked for reclamation by the GC

메모리 관리의 목적은 다음과 같습니다.

public void WasteMemory()
{
    var instance = new MyCollection(); // this one has your Dispose()
    instance.FillItWithAMillionStrings();
    instance.Dispose();
}

// 1 million strings are in memory, but marked for reclamation by the GC

만약 당신이 정말로 지금 당장 메모리를 해방시켜야 한다면, 전화하세요.GC.Collect()하지만 여기서 이렇게 할 이유는 없습니다.필요할 때 메모리가 해방됩니다.

한다면MyCollection어차피 쓰레기가 수거될 테니 폐기할 필요는 없습니다.이렇게 하면 CPU가 필요 이상으로 회전하고 가비지 수집기가 이미 수행한 일부 사전 계산된 분석이 무효화될 수 있습니다.

사용합니다IDisposable관리되지 않는 리소스와 함께 스레드가 올바르게 배치되었는지 확인하는 작업을 수행합니다.

편집 스콧의 논평에 대한 답변:

GC 성능 메트릭이 영향을 받는 경우는 [sic] GC를 호출할 때뿐입니다.수집()이 작성되었습니다."

개념적으로 GC는 객체 참조 그래프의 뷰와 스레드의 스택 프레임에서 참조되는 모든 뷰를 유지합니다.이 힙은 상당히 크고 메모리 페이지에 걸쳐 있을 수 있습니다.최적화를 위해 GC는 페이지를 불필요하게 다시 검색하지 않기 위해 자주 변경될 가능성이 없는 페이지 분석을 캐시합니다.GC는 페이지의 데이터가 변경될 때 커널로부터 알림을 수신하므로 페이지가 더럽다는 것을 알고 다시 검색해야 합니다.컬렉션이 Gen0에 있는 경우 페이지의 다른 항목도 변경될 수 있지만 Gen1 및 Gen2에서는 변경될 가능성이 적습니다.일화적으로, 이러한 후크는 플랫폼에서 Silverlight 플러그인을 작동시키기 위해 GC를 Mac으로 포팅한 팀이 Mac OS X에서 사용할 수 없었습니다.

자원의 불필요한 폐기에 반대하는 또 다른 요점은 프로세스가 언로드되는 상황을 상상하는 것입니다.또한 프로세스가 얼마 동안 실행되고 있다고 상상해 보십시오.해당 프로세스의 메모리 페이지 중 상당수가 디스크로 스왑되었을 가능성이 있습니다.최소한 L1 또는 L2 캐시에는 더 이상 없습니다.이러한 상황에서 언로드 중인 애플리케이션이 프로세스가 종료되면 모든 데이터와 코드 페이지를 메모리로 다시 스왑하여 운영 체제에서 해제할 리소스를 '해제'할 필요가 없습니다.이는 관리되는 리소스와 특정 관리되지 않는 리소스에도 적용됩니다.백그라운드가 아닌 스레드를 활성 상태로 유지하는 리소스만 배치해야 합니다. 그렇지 않으면 프로세스가 활성 상태로 유지됩니다.

이제 정상적으로 실행되는 동안 관리되지 않는 메모리 누수를 방지하기 위해 @fezmonkey가 데이터베이스 연결, 소켓, 창 핸들을 가리키듯이 사용 후 삭제 리소스를 올바르게 정리해야 합니다.이런 종류의 것들은 폐기되어야 합니다.만약 당신이 스레드를 소유하는 클래스를 만든다면(그리고 자신의 것에 의해), 적어도 내 코딩 스타일에 의해 그것이 멈추도록 보장할 책임이 있다는 것을 의미합니다), 그 클래스는 아마도 구현되어야 합니다.IDisposable그리고 그 동안에 실을 찢어 버립니다.Dispose.

.를 합니다.NET 프레임워크는 다음을 사용합니다.IDisposable인터페이스는 이 클래스를 폐기해야 한다는 것을 개발자들에게 알리는 신호, 심지어 경고로 사용됩니다.프레임워크에서 구현하는 유형을 생각할 수 없습니다.IDisposable(명시적인 인터페이스 구현 제외) 폐기는 선택 사항입니다.

관리되지 않는 리소스를 사용하거나 해제하는 것에 대해 모두 다루었던 일반적인 내용을 반복하지 않겠습니다.하지만 저는 일반적인 오해로 보이는 것을 지적하고 싶습니다.
과 같은 코드가 .

공용 클래스 대형 물건ID 일회용 구현개인 _ 문자열로 큼()
Large는 이제 수백만 개의 긴 문자열을 포함하고 있습니다.

Public Sub Dispose() - ID를 구현합니다.처분_대=없음끝 하위 항목

일회용 구현이 현재 지침을 따르지 않는다는 것을 알고 있지만, 여러분 모두가 아이디어를 얻기를 바랍니다.
이제 Dispose가 호출되면 메모리가 얼마나 확보됩니까?

답변: 없음.
폐기를 호출하면 관리되지 않는 리소스를 해제할 수 있으며 관리되는 메모리를 회수할 수 없으며 GC만 이를 수행할 수 있습니다.그렇다고 해서 위의 패턴을 따르는 것이 좋은 생각이 아니라는 것은 아닙니다.Dispose가 실행되면 LargeStuff의 인스턴스가 여전히 범위 내에 있을 수 있지만 _Large에서 사용하던 메모리를 다시 할당하는 GC를 중지할 수 없습니다._Large의 문자열도 gen 0에 있을 수 있지만 LargeStuff의 인스턴스는 gen 2일 수 있으므로 메모리는 더 빨리 재할당됩니다.
그러나 위에 표시된 Dispose 메서드를 호출하기 위해 피니셔를 추가하는 것은 의미가 없습니다.그러면 피니시저가 실행될 수 있도록 메모리 재확보가 지연될 뿐입니다.

당신이 올린 예에서, 그것은 여전히 "지금 메모리를 해방시키지 못합니다."모든 메모리는 가비지 수집되지만 이전 세대에서 메모리를 수집할 수 있습니다.확실히 하려면 몇 가지 테스트를 해야 합니다.


프레임워크 설계 지침은 지침이지 규칙이 아닙니다.인터페이스의 기본 용도, 사용 시기, 사용 방법 및 사용하지 않을 시기를 알려줍니다.

나는 ID 일회용을 사용하여 장애 시 간단한 롤백() 코드를 읽은 적이 있습니다.는 Dispose하고 MiniTx가 Dispose()일 플래그를 확인합니다.Commit전화는 일어나지 않았습니다. 그러면 전화할 것입니다.Rollback저로절 코드를 더 쉽게하도록 간접 레이어를 했습니다.그것은 호출 코드를 훨씬 더 쉽게 이해하고 유지할 수 있게 하는 간접 레이어를 추가했습니다.과 같습니다.

using( MiniTx tx = new MiniTx() )
{
    // code that might not work.

    tx.Commit();
} 

타이밍/로깅 코드가 동일한 작업을 수행하는 것도 보았습니다.이 경우 Dispose() 메서드는 타이머를 중지하고 블록이 종료되었음을 기록합니다.

using( LogTimer log = new LogTimer("MyCategory", "Some message") )
{
    // code to time...
}

관리되지 않는 리소스 정리를 수행하지 않고 ID를 성공적으로 사용하여 더 깨끗한 코드를 생성하는 몇 가지 구체적인 예가 있습니다.

지금 바로 삭제하려면 관리되지 않는 메모리를 사용하십시오.

참조:

오히려 코드를 생략할 때보다 코드의 효율성이 떨어질 것으로 예상합니다.

Clear() 메서드를 호출할 필요가 없으며, Dispose가 수행하지 않았다면 GC가 그렇게 하지 않았을 것입니다.

시스템 리소스의 수명을 제어하는 방법으로 주로 사용되는 것 외에도(Ian, kudos!의 멋진 답변에 완전히 포함됨), IDisposable/using 콤보는 콘솔, 스레드, 프로세스, 애플리케이션 인스턴스와 같은 모든 글로벌 개체와 같은 (중요한) 글로벌 리소스의 상태 변경 범위를 지정하는 도 사용할 수 있습니다.

저는 이 패턴에 대한 기사를 썼습니다: http://pragmateek.com/c-scope-your-global-state-changes-with-idisposable-and-the-using-statement/

콘솔 색상, 현재 스레드 문화, Excel 응용 프로그램 개체 속성자주 사용되는 글로벌 상태를 재사용 가능하고 읽을 수 있는 방법으로 보호하는 방법을 설명합니다.

관리되는 리소스와 관리되지 않는 리소스 모두에 대해 ID를 사용하는 것에 대한 많은 답변이 바뀌었습니다.저는 이 기사를 일회용 ID가 실제로 어떻게 사용되어야 하는지에 대한 가장 좋은 설명 중 하나로 제안하고 싶습니다.

https://www.codeproject.com/Articles/29534/IDisposable-What-Your-Mother-Never-Told-You-About

실제 질문의 경우 ID Disposable을 사용하여 메모리를 많이 사용하는 관리 개체를 정리하는 경우에는 간단히 "아니오"라고 대답합니다.그 이유는 메모리를 보유하고 있는 개체가 범위를 벗어나면 수집 준비가 완료되기 때문입니다.이 시점에서 참조된 하위 개체도 범위를 벗어나 수집됩니다.

관리되는 개체에 많은 메모리가 연결되어 있고 일부 작업이 완료되기를 기다리는 동안 해당 스레드를 차단한 경우에만 예외가 될 수 있습니다.호출이 완료된 후 필요하지 않은 개체를 null로 설정하면 가비지 수집기에서 개체를 더 빨리 수집할 수 있습니다.그러나 그 시나리오는 ID 일회용 사용 사례가 아니라 리팩터링이 필요한 잘못된 코드를 나타낼 것입니다.

IDisposable이벤트 가입을 취소하는 데 좋습니다.

은 지정코샘다은대음좋한예다아니닙가은의 좋은 .IDisposable사전 지우기는 일반적으로 다음으로 가지 않아야 합니다.Dispose例방.됩니다.사전 항목이 범위를 벗어나면 삭제되고 삭제됩니다. IDisposable범위를 벗어난 후에도 해제/해제되지 않는 일부 메모리/장치를 해제하려면 구현이 필요합니다.

다음 예제에서는 일부 코드와 주석이 포함된 ID 일회용 패턴의 좋은 예를 보여 줍니다.

public class DisposeExample
{
    // A base class that implements IDisposable. 
    // By implementing IDisposable, you are announcing that 
    // instances of this type allocate scarce resources. 
    public class MyResource: IDisposable
    {
        // Pointer to an external unmanaged resource. 
        private IntPtr handle;
        // Other managed resource this class uses. 
        private Component component = new Component();
        // Track whether Dispose has been called. 
        private bool disposed = false;

        // The class constructor. 
        public MyResource(IntPtr handle)
        {
            this.handle = handle;
        }

        // Implement IDisposable. 
        // Do not make this method virtual. 
        // A derived class should not be able to override this method. 
        public void Dispose()
        {
            Dispose(true);
            // This object will be cleaned up by the Dispose method. 
            // Therefore, you should call GC.SupressFinalize to 
            // take this object off the finalization queue 
            // and prevent finalization code for this object 
            // from executing a second time.
            GC.SuppressFinalize(this);
        }

        // Dispose(bool disposing) executes in two distinct scenarios. 
        // If disposing equals true, the method has been called directly 
        // or indirectly by a user's code. Managed and unmanaged resources 
        // can be disposed. 
        // If disposing equals false, the method has been called by the 
        // runtime from inside the finalizer and you should not reference 
        // other objects. Only unmanaged resources can be disposed. 
        protected virtual void Dispose(bool disposing)
        {
            // Check to see if Dispose has already been called. 
            if(!this.disposed)
            {
                // If disposing equals true, dispose all managed 
                // and unmanaged resources. 
                if(disposing)
                {
                    // Dispose managed resources.
                    component.Dispose();
                }

                // Call the appropriate methods to clean up 
                // unmanaged resources here. 
                // If disposing is false, 
                // only the following code is executed.
                CloseHandle(handle);
                handle = IntPtr.Zero;

                // Note disposing has been done.
                disposed = true;

            }
        }

        // Use interop to call the method necessary 
        // to clean up the unmanaged resource.
        [System.Runtime.InteropServices.DllImport("Kernel32")]
        private extern static Boolean CloseHandle(IntPtr handle);

        // Use C# destructor syntax for finalization code. 
        // This destructor will run only if the Dispose method 
        // does not get called. 
        // It gives your base class the opportunity to finalize. 
        // Do not provide destructors in types derived from this class.
        ~MyResource()
        {
            // Do not re-create Dispose clean-up code here. 
            // Calling Dispose(false) is optimal in terms of 
            // readability and maintainability.
            Dispose(false);
        }
    }
    public static void Main()
    {
        // Insert code here to create 
        // and use the MyResource object.
    }
}

그들이 하는 일들이 있습니다.Dispose()작업은 예제 코드에서 수행되며, 이 예제 코드는 일반 GC로 인해 발생하지 않을 수 있습니다.MyCollection물건.

에서 _theList또는_theDict다른 객체에 의해 참조됩니다. 그러면List<>또는Dictionary<>개체가 수집 대상이 되지 않지만 갑자기 내용이 없습니다.예제와 같이 폐기() 작업이 없는 경우에도 이러한 컬렉션에는 내용이 포함됩니다.

이라면, 저는 디자인이라고 입니다. (입니다. 물론, 만이상라면이, 시그을깨것부 - 는저단지다니다입지것적하는니입저진생에는를것디고자인이로학적제각으라황런약시▁of▁the▁-▁pointing▁out▁(▁i▁situation다▁it▁if▁i▁this▁the지ped▁design)물▁suppose것론▁a▁call▁brokenm▁wereantically▁i적▁just니하입,는▁would,,▁course저으'저▁that단로▁(,적▁-생)는는제Dispose()다른 용도가 있는지 여부에 따라 운영이 완전히 중복되지 않을 수 있습니다.List<>또는Dictionary<>조각에는 표시되지 않습니다.

"관리되지 않는 리소스"에 대한 대부분의 논의에서 한 가지 문제는 용어를 실제로 정의하지는 않지만 관리되지 않는 코드와 관련이 있음을 암시한다는 것입니다.많은 유형의 관리되지 않는 리소스가 관리되지 않는 코드와 상호 작용하는 것은 사실이지만 관리되지 않는 리소스를 이러한 용어로 생각하는 것은 유용하지 않습니다.

대신, 모든 관리되는 자원의 공통점을 인식해야 합니다. 모든 자원은 외부의 어떤 '무엇'에게 자신을 대신하여 무언가를 해달라고 요청하는 객체를 수반하고, 다른 '무엇'에게 피해를 주고, 다른 기업은 추후 통지가 있을 때까지 그렇게 하기로 동의합니다.만약 그 물체가 버려지고 흔적도 없이 사라진다면, 그 어떤 것도 더 이상 존재하지 않는 물체를 대신하여 그것의 행동을 바꿀 필요가 없다는 것을 '사물' 밖에서 결코 말하지 않을 것입니다. 결과적으로, '사물의 유용성'은 영구적으로 감소할 것입니다.

따라서 관리되지 않는 자원은 객체를 대신하여 행동을 변경하는 외부의 '물건'에 의한 합의를 나타내며, 객체가 버려지고 더 이상 존재하지 않는 경우 외부의 '물건'의 유용성을 쓸모없게 손상시킬 것입니다.관리되는 리소스는 해당 계약의 수혜자이지만 이를 포기할 경우 알림을 수신하기 위해 등록한 개체이며, 이 개체가 파기되기 전에 해당 알림을 사용하여 해당 작업을 정리합니다.

정의의 첫 번째.관리되지 않는 리소스는 ID 일회용 인터페이스나 dll 호출을 사용하여 만든 클래스를 의미합니다.GC는 그런 물건을 다루는 법을 모릅니다.예를 들어 클래스에 값 유형만 있는 경우 이 클래스를 관리되지 않는 리소스가 있는 클래스로 간주하지 않습니다.내 코드에 대해서는 다음 관행을 따릅니다.

  1. 에서 생성된 되지 않는 .me 을 하기 위해 해야 한다는 입니다.
  2. 사용이 완료되는 대로 개체를 청소합니다.
  3. 처분 방법에서는 모든 ID 처분 가능한 클래스 구성원에 대해 반복하고 처분을 요청합니다.
  4. 를 호출합니다. "Dispose"는 GC입니다.가비지 수집기에 내 개체가 이미 정리되었음을 알리려면 Finalize(마침)를 누릅니다.GC 호출은 비용이 많이 들기 때문에 그렇게 합니다.
  5. 추가적인 예방책으로 Dispose()를 여러 번 호출하려고 합니다.
  6. 때때로 나는 개인 멤버_disposed를 추가하고 method calls do 객체가 정리된 것을 체크인합니다.정리된 경우 ObjectDisposed를 생성예외
    다음 템플릿은 제가 단어로 코드 샘플로 설명한 것을 보여줍니다.

public class SomeClass : IDisposable
    {
        /// <summary>
        /// As usually I don't care was object disposed or not
        /// </summary>
        public void SomeMethod()
        {
            if (_disposed)
                throw new ObjectDisposedException("SomeClass instance been disposed");
        }

        public void Dispose()
        {
            Dispose(true);
        }

        private bool _disposed;

        protected virtual void Dispose(bool disposing)
        {
            if (_disposed)
                return;
            if (disposing)//we are in the first call
            {
            }
            _disposed = true;
        }
    }

관리되는 리소스를 폐기하는 가장 정당한 사용 사례는 GC가 수집되지 않은 리소스를 회수할 수 있도록 준비하는 것입니다.

대표적인 예는 순환 참조입니다.

순환 참조를 피하는 패턴을 사용하는 것이 최선의 방법이지만, (예를 들어) '부모'에 대한 참조가 있는 '자녀' 개체로 끝나는 경우 참조를 버리고 GC에 의존하는 경우 부모의 GC 수집을 중지할 수 있습니다. 게다가 최종 사용자를 구현한 경우에는 이 개체가 호출되지 않습니다.

이 문제를 해결할 수 있는 유일한 방법은 상위 참조를 자식에 대해 null로 설정하여 순환 참조를 수동으로 끊는 것입니다.

부모와 자녀에게 사용할 수 있는 ID를 구현하는 것이 가장 좋은 방법입니다.상위 항목에서 삭제를 호출하면 모든 하위 항목에 대해 삭제를 호출하고 하위 항목 삭제 방법에서 상위 항목을 null로 설정합니다.

저는 사람들이 ID 일회용 패턴을 관리되지 않는 자원을 정리하는 것을 돕기 위한 ID 일회용의 주요 목적과 혼동하고 있다고 생각합니다.우리 모두는 이것을 알고 있습니다.어떤 사람들은 그 패턴이 기억을 지우고 자원을 해방시키는 일종의 마법의 힘을 가지고 있다고 생각합니다.패턴은 이 작업을 수행하지 않습니다.그러나 구현된 방법으로 패턴을 사용하면 메모리를 지우고 리소스를 확보할 수 있습니다.

패턴은 단순하게 기본 제공 try{} finally{} 블록입니다.그 이상은 아닙니다.그에 못지 않습니다.그게 무슨 뜻이죠?코드 블록을 만들어 추가 코드를 수행할 필요 없이 마지막에 작업을 수행할 수 있습니다.코드와 범위를 분할하는 데 사용할 수 있는 사용자 정의 블록을 제공합니다.

예:

//My way
using (var _ = new Metric("My Test"))
{
    DoSomething();  //You now know all work in your block is being timed.
}

//MS mockup from memory
var sw = new Stopwatch();
sw.Start();
DoSomething();  //something fails? I never get the elapsed time this way
sw.Stop();

메트릭 클래스

    public class Metric : IDisposable
    {
        private string _identifier;
        private DateTime _start;

        public Metric(string identifier)
        {
            _identifier = identifier;
            _start = DateTime.Now;
        }

        public void Dispose()
        {
            Console.WriteLine(_identifier + " - " + (DateTime.Now - _start).TotalMilliseconds)
        }
    }

언급URL : https://stackoverflow.com/questions/538060/proper-use-of-the-idisposable-interface

반응형