1

私のアプリケーションのライブラリでは、管理されていないリソースをグローバルに使用する必要があります。

これを容易にするために、ライブラリには次のものがあります。

  • から継承するシングルトンクラスがありIDisposableます。
  • アンマネージリソースの取得はInstance()、クラスのメソッドで行われます。
  • リソースは常にMySingletonオブジェクトを介してアクセスされるため、これは、必要なときにアンマネージリソースを確実に使用できるようにする論理的な方法のようです。

    public class MySingleton : IDisposable
    {
        private static MySingleton instance;
    
        public static MySingleton Instance
        {
            get
            {
                if (instance == null)
                {
                    lock (typeof(MySingleton))
                    {
                        if (instance == null)
                        {
                            instance = new MySingleton();
    
                            // Acquire unmanaged resource here
                        }
                    }
                }
    
                return instance;
            }
        }
    
        public void Dispose()
        {
            // Release unmanaged resource here
        }
    }
    

    問題

    • 上記のシングルトンを備えたライブラリは、複数のアプリケーションで使用されています。
    • 管理されていないリソースが正しくクリーンアップされるようにするには、MySingleton.Instance.Dispose()各アプリケーション内で呼び出す必要があります(通常、これがすべての状況finallyで発生することを確認するためにブロック内で)。exception
    • 別のアプリケーション(つまり、エントリポイント)が作成され、作成者がDisposeメソッドの呼び出しを忘れた場合、管理されていないリソースが適切にクリーンアップされない可能性があります。

    これを行うためにクラスにデストラクタを追加しようとしましたMySingletonが、アプリケーションの終了時にブレークポイントがヒットしていないようです。これは、GCが非決定的であり、静的オブジェクトのデストラクタが破棄される前にアプリケーションが終了しているためだと思いますが、よくわかりません。

    これが確実に行われるようにクライアントアプリケーションが明示的に呼び出すことに依存せずに、このリソースが常にクリーンアップされるようにするための洗練された方法はありますDisposeか?

  • 4

    3 に答える 3

    2

    シングルトンへの静的参照は、他の誰かが実際にそれを使用しているかどうかに関係なく、それを存続させます。シングルトンへの外部参照がすべて消えてしまったことを自分で確認したい場合は、次のいずれかを実行する必要があります。

    1. シングルトンへの`WeakReference`を保持し、誰かがオブジェクトインスタンスを要求するたびにそれを返します。長い`WeakReference`と短い`WeakReference`の両方を保持したい場合があります。オブジェクトがファイナライズの対象になるとすぐに、短いものは無効になります。長いものは、オブジェクトがファイナライズされた後のGCサイクルまで有効です。短い`WeakReference`が無効になると、長い弱い参照があり、ファイナライザーがまだ実行されていない場合でも、新しいインスタンスを作成する必要があります。一方、古いオブジェクトでファイナライザーが要求されてから実際に実行されるまでの間に新しいオブジェクトが要求された場合を処理するには、長い`WeakReference`が必要になる場合があります。
    2. 「実際の」シングルトンへの強い参照を保持しますが、誰にも公開しないでください。代わりに、ラッパーを作成し、それに`WeakReference`を格納します。ラッパーのファイナライザーは、シングルトンがそれ自体をクリーンアップするように要求する必要があります。このアプローチは、古いオブジェクトがファイナライズの対象になってから実際にファイナライズが行われるまでの間にコードがシングルトンインスタンスを要求するシナリオの最初のアプローチよりも安全な場合があります。すべてのクリーンアップが単一の`Finalize`メソッドを介して行われる場合、get-singletonメソッドは、ファイナライザーにクリーンアップをスキップするように要求し、要求が時間内に行われたかどうかを知ることができます(そうでない場合は、新しいインスタンスを作成する前に、ファイナライザーが完了するのを待つ必要があります)。
    3. 「実際の」シングルトンへの強い参照を維持し、それを要求するすべての異なるエンティティに異なるラッパーを与えます。まだ`finalize`が呼び出されていないラッパーのリストを保持し(ラッパー自体への弱参照を保持しますが、ラッパーに関する情報への強参照を保持します)、リストが空の場合はクリーンアップを実行します。このアプローチは2番目のアプローチよりも複雑になりがちですが、オブジェクトへの参照を保持しているユーザーを特定することができます。

    古いインスタンスが存在する間はリソースを再作成できないリソースである場合は、通常、アプローチ#2をお勧めします。アプローチ#1の問題は、リソースに関連付けられたファイナライズ可能なオブジェクトが複数ある場合、リソースが部分的にクリーンアップされるかなりの間隔がある可能性があるため、コードは他のオブジェクトを待つしかありません。 'ファイナライザーを完了します。アプローチ#2を使用する場合、ファイナライザーが保留中にシングルトンが要求されると、ファイナライザーがクリーンアップを実行できないように、どちらかの要求がすぐに届きます(この場合、ファイナライザーが実行されるのを待つ必要はありません。実際には何でもします)、ファイナライザーが実行された後に実行されます(この場合、新しいインスタンスをすぐに作成できます)。

    于 2012-07-13T15:42:49.723 に答える
    1

    シングルトンにIDisposableを実装させるのは間違っているように聞こえます。オブジェクトの処分の責任者は誰ですか?

    2つのスレッドまたは2つのプロセスがリソースにアクセスしようとするとどうなるでしょうか。敗者は例外をブロックまたはスローする必要がありますか?

    IDisposableを実装し、リソースが使用されている間だけ存続することを目的としたクラスがある場合は、RAIIアプローチを採用する方がよいと思います。

    class MyResource : IDisposable { 
      public MyResource() {
        // Acquire resource here
      }
    
      public void Dispose() {
        // Free resource here, along with extra stuff to attempt
        // to catch situations where its not disposed 
      }
    }
    

    それで:

    using( var r = new MyResource() ) {
      // Do work here.
    }
    

    ライブラリを使用している他のすべての開発者がクラスを正しく破棄することを保証することはできませんが、usingステートメント内でクラスを使用しなかった人は誰でも大きな鋭い棒で教育することができます。

    于 2012-07-12T07:17:24.633 に答える
    0

    デストラクタがヒットしていないことについては、次を参照してください。http: //msdn.microsoft.com/en-us/library/66x5fx1b.aspx デストラクタが呼び出されますが、デバッグ中には表示されないと思います。シングルトンについては、静的であるため、プログラムが終了するまで呼び出されないことは明らかです。

    Disposeが呼び出されるようにすることについて、あなたができる最善のことはIDisposableを継承し、disposeの必要性を文書化することだと思います。物事を自動的に処理する方法があったとしたら、MSはそれをSqlConnectionまたは他の使い捨てクラスに対して行ったと思いませんか?ところで、インスタンスがクラス自体にいつ破棄されるかを決定するのは良い習慣ではないと思います。このようにして、クラスの使用を制限し、クラスを使用する他のコンポーネントが何をしたいかを知ることができません。

    于 2012-07-12T06:57:26.243 に答える