8

C# のマネージド リソースとアンマネージド リソースについて友人と話し合いました。

私の友人によると:

1.a) C# のすべてのオブジェクトは管理されており、C# でコーディングする場合、管理されていないオブジェクトやリソースのようなものはありません。アンマネージ リソースの概念は、C++ にのみ付属しています。

1.b) C++ で管理されたリソースまたは管理されていないリソースがあるかどうかにかかわらず、明示的に解放する必要があります。C# には自動ガベージ コレクターがあるため、リソースの管理について考える必要はありません。

私によると:

2.a) 管理されていないリソースがない場合、なぜ C# でファイナライザーまたは Dispose メソッドが必要なのですか?

2.b) ガベージ コレクターには、割り当てられたメモリに関する情報のみがあり、リソースの状態に関する情報はありません。したがって、C# でリソースを解放するには、dispose メソッドを使用する必要があります。

上記の引数のどれが正しいか、および c# のアンマネージ リソースに関する情報が存在するかどうかを理解するのに助けが必要ですか?

前もって感謝します。

4

7 に答える 7

10

いいえ、アンマネージ リソースを使用せずに C# プログラムを作成することは不可能です。C# プログラムは、100% 管理されていないオペレーティング システムで実行されることは避けられません。ファイルを使用する場合は、オペレーティング システム リソースを使用します。ネットワーク接続。スレッド。コンソール。など、すべて非常に管理されていないリソースです。

しかし、その事実は .NET ではかなりうまく隠されています。フレームワーク ライブラリには、これらのネイティブ オブジェクト用の優れたラッパー クラスがあります。FileStream、ソケット、スレッド、コンソールなど。メモリもオペレーティング システムのリソースであり、ガベージ コレクタはその周りのラッパーです。

これらすべてのリソースのうち、メモリ リソースだけが真に自動的に管理されます。それらの残りの部分は、ラッパー クラスのおかげである程度の助けになります。それらのファイナライザーが鍵であり、呼び出されたときにオペレーティング システムのリソースを解放します。これはほぼ自動です。ガベージ コレクターは、ラッパー クラス オブジェクトがどこにも参照されていないことに気づき、それを解放します。その後、ファイナライザーは、アンマネージ リソースも解放されるようにします。

これは通常はうまく機能しますが、多くの場合、コード内でこれらの実装の詳細を無視できます。多くのプログラマーがそうしています。

ただし、ファイナライザーには問題があり、実行を開始するのに時間がかかります。それらを開始するにはガベージ コレクションが必要で、数ミリ秒から数分かかる場合があります。これは予測不可能であり、コードでメモリを消費する速度に依存します。たくさん使わないと、かなり時間がかかります。

管理されていないリソースが解放されるまで、それほど長く待つ余裕があるとは限りません。ファイルが良い例です。ファイルからデータを読み取るためにファイルを開いた場合、読み取りが完了したらファイルを閉じる必要があります。ファイナライザーがそのジョブを完了するまで待つと、しばらくしてからファイルを再度開く必要があるときに、プログラムが失敗するリスクがあります。FileShare.None でファイルを開くと、自分自身のコードもロックアウトされます。大したことではありません。読み取りが完了したら、Close() を呼び出してファイルを閉じます。確実に閉じられるようにするには、Close() 呼び出しを finally ブロックに配置して、コードが例外によって中止された場合でも実行されるようにする必要があります。実際には、ファイナライザー コードを明示的に実行します。

より深刻なケースは、非常に高価なオペレーティング システム リソースです。それらの良い例はビットマップで、大量のアンマネージ メモリやデータベース接続を使用する可能性があります。デフォルトでは 100 個しか含まれないプールがあります。これらの場合、ファイナライザーにリソースの解放を任せても、時間がかかりすぎてうまくいかない状況に陥る可能性があります。ファイナライザーが実行される前に、プログラムが例外で終了します。プログラムに負荷がかかっているときにのみ発生する傾向があるため、通常、診断はかなり困難です。デスクトップ上にないマシンで多くのことが起こっているときに発生する問題をデバッグするのは常に困難です。

.NET の設計者はこの必要性を認識し、IDisposable インターフェイスを設計しました。その Dispose() メソッドは、ファイナライザーによって通常実行されるコードを実行するように設計されており、ガベージ コレクターがリソースに到達するのを待つのではなく、明示的にリソースを解放する方法を提供します。そして、言語設計者は、IDisposable.Dispose() が自動的に呼び出されるように、 usingキーワードを言語に追加することで、その流行に飛び乗りました。

上記で説明したように、IDisposable を実装する任意のオブジェクトのコードで using または Dispose() を使用することはオプションですが、多くの .NET プログラマーによって重要であると考えられていますほとんどの場合、誰もがそれなしで .NET プログラミングを開始し、プログラムが大きくなると遅かれ早かれ問題に遭遇したためです。MemoryStream のように、Dispose() の呼び出しが意味をなさないクラスにも規定されています。また、Thread のように、クラスが IDisposable を実装する必要があるのに実装しない場合、精神的苦痛を引き起こします。または、クラスが Dispose と Close の両方を実装する場合 (違いはありません)。比較のために、Java にも同じ考慮事項がありますが、IDisposable はありません。

于 2011-06-17T11:54:06.627 に答える
3

.NETで作成されたオブジェクトはマネージコードですが、オブジェクトはアンマネージリソースへの参照を保持できます。ガベージコレクター(GC)は、管理対象ヒープに割り当てられたメモリが不要になった後、確実にクリーンアップされます。ただし、ガベージコレクターはメモリがリークしないようにするのに優れていますが、解放する必要のある他のリソースについての知識はありません。たとえば、ガベージコレクターは、ファイルハンドルを閉じる方法や、CoAllocTaskMemなどのAPIを使用してマネージヒープの外部に割り当てられたメモリを解放する方法を知りません。

これらのタイプのリソースを管理するオブジェクトは、それらが不要になったときにそれらが解放されることを保証する必要があります。これを実現するには、System.ObjectのFinalizeメソッドをオーバーライドします。これにより、ガベージコレクターは、オブジェクトが独自のクリーンアップに参加することを通知します(C#では、メソッドを直接オーバーライドするのではなく、C ++デストラクタ構文〜MyObjectを使用します)。 )。クラスにファイナライザーがある場合、そのタイプのオブジェクトが収集される前に、ガベージコレクターはオブジェクトのファイナライザーを呼び出し、保持している可能性のあるすべてのリソースをクリーンアップできるようにします。

このシステムの問題の1つは、ガベージコレクターが決定論的に実行されないため、オブジェクトへの最後の参照がなくなった後、オブジェクトが長期間ファイナライズされない可能性があることです。オブジェクトがデータベース接続などの高価またはまれなリソースを保持している場合、これは受け入れられない可能性があります。たとえば、オブジェクトで使用可能な接続が10個のうち1個だけ開いている場合、ガベージコレクタがfinalizeメソッドを呼び出すのを待つのではなく、できるだけ早くその接続を解放する必要があります。

そのためには、実装する必要のあるIDisposableインターフェイスがあります。それについてもっと読む。

于 2011-06-17T08:42:39.433 に答える
3

a)C#のすべてのオブジェクトは管理されており、C#でコーディングする場合、管理されていないオブジェクトやリソースのようなものはありません. アンマネージ リソースの概念は、C++ にのみ付属しています。

これは正しくありません。他の人が述べたように、C# (COM など) の外部からアンマネージ リソースを取得できます。

ただし、アンマネージ コードにアクセスせずに、C# で "アンマネージ リソース" を使用することは確かに可能です。これらのリソースは、ガベージ コレクションの厳密な意味で管理されていない可能性がありますが、開発者としてクリーンアップを処理する必要があるリソースです。たとえば、スレッドを見てみましょう:

class Foo
{
    private Thread thread = new Thread(new ThreadStart(DoLotsOfWork));
    private AutoResetEvent endThread = new AutoResetEvent(false);
    private int sum = 0;

    public Foo()
    {
        thread.Start();
    }

    public StopThread()
    {
        endThread.Set();
    }

    private void DoLotsOfWork()
    {
        while (!endThread.WaitOne(1000))
        {
            sum += 1;
        }
    }
}

static void Main(string[] args)
{
    Foo foo = new Foo();
    // Additional code...
    foo.StopThread();
}

追加のコードが例外を返すかスローするとします。StopThread を明示的に呼び出さないと、DoLotsOfWork を実行しているスレッドが終了せず、プ​​ロセスが終了しない可能性があります。

b) C++ で管理されたリソースまたは管理されていないリソースがあるかどうかにかかわらず、明示的に解放する必要があります。C# には自動ガベージ コレクターがあるため、リソースの管理について考える必要はありません。

C# でリソースを管理することを絶対に考慮する必要があります。あなたが示唆するように、これが IDisposable が存在する理由です。

上記のコードに対する次の変更を検討してください。

class Foo : IDisposable
{
    private bool disposed = false;
    private Thread thread = new Thread(new ThreadStart(DoLotsOfWork));
    private AutoResetEvent endThread = new AutoResetEvent(false);
    private int sum = 0;

    public Foo()
    {
        thread.Start();
    }

    public StopThread()
    {
        endThread.Set();
    }

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

    private void DoLotsOfWork()
    {
        while (!endThread.WaitOne(1000))
        {
            sum += 1;
        }
    }

    private void Dispose(bool disposing)
    {
        if (!disposed && disposing)
        {
            StopThread();
            disposed = true;
        }
    }
}

static void Main(string[] args)
{
    using (Foo foo = new Foo())
    {
        // Additional code...
    }
}

これで、追加のコードが何を行っても、プロセスが終了する前に Foo クラスによって作成されたスレッドが停止されることを確認できます。

于 2011-06-17T11:25:19.497 に答える
2

.net Frameworkで作成するものはすべて管理されたコードであるため、.netFrameworkManagerをフレームワークでのみ使用して作成されたオブジェクトによってメモリが消費されます。

.netフレームワークの外部で作成されるものはすべて、アンマネージコードです。

ファイルを閉じる必要がある場合やグラフィックを使用していて、それに関連付けられているメモリを解放したい場合など、hevyオブジェクトを解放する場合は、finalizerまたはdisposeを使用できます。このメソッドを使用できます。

于 2011-06-17T08:43:01.617 に答える
1

確かに、CLRで作成されたすべてのオブジェクトはCLRによって管理されるため、それらを気にする必要はありません。

ただし、CLRの外部のリソース(たとえば、COMオブジェクト、またはデバイスのロック)の使用を開始する場合は、それらのリソースを解放するのはユーザーの責任です。CLRはそれを実行できませんIDisposableが、クリーンアップを実行するコードを記述できるようにするためのインターフェイスを提供します。

于 2011-06-17T08:44:13.837 に答える
1

.NET で管理されていないリソースを処理する必要があります。良い例は、データベースへの接続です。

そのアンマネージ リソースを明示的に閉じる必要があります。これは、DisposeC# を使用する理由の例です。

于 2011-06-17T08:45:22.960 に答える
1

管理されていないリソースを保持するオブジェクトは、不要になったと通知されるまで、他のエンティティをやや望ましくない状態にします (たとえば、他の目的で使用できなくするなど)。不要になったことが最初に通知されずに放棄された場合、それらの他のエンティティは望ましくない状態のままになります。

管理されたリソースは、不要になったと言われるまで、他のエンティティを同様にやや望ましくない状態にするエンティティですが、それに対するすべての「意図的な」参照が放棄されると、最終的には自動的にクリーンアップされます。

有効期間の長いオブジェクトからのイベント サブスクリプションは、マネージド コード内に完全に存在しますが、有効期間の長いオブジェクトの有効期間中に自動的にクリーンアップされないため、アンマネージド リソースと見なす必要があります。

于 2011-06-17T18:11:50.707 に答える