4

次のような C++0x のメッセージ ポンプ クラスがあるとします (注、SynchronizedQueue は function<void()> のキューであり、キューで receive() を呼び出して空になると、呼び出し元のスレッドがブロックされるまで返品するアイテムがあります):

class MessagePump
{
 private:
    bool done_;
    Thread* thread_;
    SynchronizedQueue queue_;

    void Run()
    {
        while (!done)
        {
            function<void()> msg = queue_.receive();
            msg();
        }
    }
 public:
    MessagePump(): 
        done_(false)
    {
        thread_ = new thread ([=] { this->Run(); } ) );
    }

    ~MessagePump()
    {
        Send( [&]{ done = true; } );
        thread_->join();
    }

    void Send (function<void()> msg)
    {
        queue_.send(msg);
    }
};

このクラスを C# に変換しましたが、デストラクタのコードについて質問があります。IDisposable パターンによると、マネージド リソースとアンマネージド リソースを解放するために Dispose() メソッドのみを提供する必要があります。

C++ デストラクタ コードを次の場所に配置する必要があります。

  1. アプリケーションの終了時にクライアントが呼び出す必要があるカスタム CleanUp() メソッドは? クライアントが忘れた場合はどうなりますか?
  2. クライアントも呼び出すことができるように、IDisposable の Dispose() メソッドはありますか? しかし、繰り返しますが、クライアントが忘れたらどうしますか?
  3. C#ファイナライザーメソッド内で常に実行されますか? 管理されていないリソースがない場合は、パフォーマンスが低下するため、ファイナライザー メソッドを含めないでください。
  4. どこにも?Thread オブジェクトは管理対象リソースであるため、done_ フラグのマークを無視して、GC に自然に処理させるだけですか? この方法でスレッドは強制的に中止されますか?

また、コンストラクター内で作成されたメッセージ ポンプ スレッドをバックグラウンド スレッドとしてマークしないと、MessagePump オブジェクトが GC されず、終了時にアプリケーションがハングすることもわかりました。これの理由は何ですか?

4

2 に答える 2

2

大まかに言えば、.NET スレッド プール ( ) を使用して複数の作業項目をキューに入れ、実行することをお勧めします (System.Threading.ThreadPool作業項目が非同期で実行できると仮定すると)。具体的には、QueueUserWorkItem方法を確認してください。

ただし、質問に答えるには:

C++ デストラクタ コードを次の場所に配置する必要があります。

アプリケーションの終了時にクライアントが呼び出す必要があるカスタム CleanUp() メソッドは? クライアントが忘れた場合はどうなりますか?

クライアントも呼び出すことができるように、IDisposable の Dispose() メソッドはありますか? しかし、繰り返しますが、クライアントが忘れたらどうしますか?

常にIDisposableカスタムCleanUpメソッドよりも実装を優先します (BCL では、一部のStreamクラスにCloseは実際には の単なるエイリアスであるメソッドがありますDispose)。このIDisposableパターンは、C# で決定論的なクリーンアップを行う方法です。クライアントが呼び出しを忘れることDisposeは常に問題ですが、これは多くの場合、静的分析ツール (FxCop など) によって検出できます。

C#ファイナライザーメソッド内で常に実行されますか? 管理されていないリソースがない場合は、パフォーマンスが低下するため、ファイナライザー メソッドを含めないでください。

ファイナライザーの実行は保証されていないため (この記事を参照)、正しいプログラムはファイナライザーが実行されるとは想定できません。ここではパフォーマンスは問題になりません。MessagePumpオブジェクトはせいぜい数個しかないと思うので、ファイナライザーを使用するコストはわずかです。

どこにも?Thread オブジェクトは管理対象リソースであるため、done_ フラグのマークを無視して、GC に自然に処理させますか? この方法でスレッドは強制的に中止されますか?

スレッドは CLR によって管理され、適切にクリーンアップされます。スレッドがエントリ ポイント (Runここ) から戻った場合、スレッドは中止されず、正常に終了します。ただし、このコードはまだどこかに行く必要があるため、IDisposable.

また、コンストラクター内で作成されたメッセージ ポンプ スレッドをバックグラウンド スレッドとしてマークしないと、MessagePump オブジェクトが GC されず、終了時にアプリケーションがハングすることもわかりました。これの理由は何ですか?

.NET アプリケーションは、すべてのフォアグラウンド (非バックグラウンド) スレッドが終了するまで実行されます。そのため、スレッドをバックグラウンド スレッドとしてマークしないMessagePumpと、実行中もアプリケーションが存続します。一部のオブジェクトがまだ を参照している場合MessagePumpMessagePumpは GC またはファイナライズされません。ただし、上記の記事をもう一度参照すると、ファイナライザーが実行されるとは限りません。

于 2010-09-16T04:40:59.983 に答える
0

役立つ可能性のあるパターンの1つは、メッセージポンプの外部ユーザーに、ポンプ自体が弱い弱参照のみを保持する「STILL INUSE」フラグオブジェクトへの強い参照を保持させることです(オブジェクトの「STILL使用中」がファイナライズの対象になります)。このオブジェクトのファイナライザーはメッセージポンプにメッセージを送信できる可能性があり、メッセージポンプはその弱参照の継続的な有効性をチェックできます。無効になった場合、メッセージポンプがシャットダウンする可能性があります。

メッセージポンプの一般的な問題の1つは、メッセージポンプを操作するスレッドが、そのスレッド以外で使用されていない多くのオブジェクトを存続させる傾向があることです。物事を確実にクリーンアップするために、スレッドが強力な参照を保持することを回避する別のオブジェクトが必要です。

于 2011-06-12T20:36:39.690 に答える