14

私のすべての読みに基づいて、すべてのファイナライザーを呼び出すための1つのGCスレッドが必要です。ここで問題となるのは、この「1つの」スレッドの範囲です。プロセスごとまたはアプリケーションドメインごとです。ドメインの全体的な目的は、1つのプロセススペースで「独立した」異なるアプリケーションを分離して作成することです。

私はここを読んだ:

ファイナライザーで未処理の例外が発生した場合、CLRの実行スレッドは例外を飲み込み、ファイナライザーを正常に完了したかのように扱い、侵害可能なキューから削除して次のエントリに移動します。

ただし、もっと深刻なのは、ファイナライザーが何らかの理由で終了しない場合、たとえば、ファイナライザーがブロックされ、決して発生しない状態を待機している場合にどうなるかです。この場合、ファイナライザスレッドがハングするため、ファイナライズ可能なオブジェクトがガベージコレクションされることはありません。この状況を十分に認識し、ファイナライザーで管理されていないリソースを解放するための最も単純なコードの記述に固執する必要があります。

もう1つの考慮事項は、アプリケーションのシャットダウン中に何が起こるかです。プログラムがシャットダウンすると、ガベージコレクターは、ファイナライズ可能なすべてのオブジェクトのファイナライザーを呼び出そうとしますが、特定の制限があります。

  • ファイナライズ可能なオブジェクトは、シャットダウン中に上位のヒープ世代にプロモートされません。

  • 個々のファイナライザーの実行時間は最大2秒です。それより長くかかる場合、それは殺されます。

  • すべてのファイナライザーが実行されるまでに最大40秒かかります。ファイナライザーがまだ実行中の場合、またはこの時点で保留中の場合、プロセス全体が突然強制終了されます。

「アプリケーション」、「プロセス」、「アプリケーションドメイン」という用語の誤用が多すぎる投稿(および公式ドキュメント)-通常、アプリケーションは単一のプロセスで単一のアプリケーションドメインで実行されるため、それらのほとんどは同等であると想定しています。 。この誤用により、これらのドキュメントはすべて読みにくくなり、役に立たなくなります。

したがって、私の質問では、複数のアプリケーションが、それぞれが単一のプロセスの個別のアプリケーションドメインで実行されることを前提としています。

これらのアプリケーションはすべて同じGCスレッドとファイナライザースレッドを共有していますか?上記の記事で説明されている問題(ファイナライザースレッドのハング)は、そのプロセスのすべてのアプリケーションに影響しますか?はいの場合-ファイナライザースレッドを検出してThread.Abortを送信するなどの回避策(悪いアプリケーションを使用しないこと以外)はありますか?

上記のすべては、私が同様の問題にぶつかったためです。私のアプリケーションは、サードパーティソフトウェア(Outlook)へのアドインとして別のアプリケーションドメインで実行されます。さまざまな理由により、COM参照を完全に解放するためにGC.CollectとGC.WaitForPendingFinalizersを呼び出す必要があります(通常の相互運用ルーチンはOffice / Outlookには不十分です)。特定の他のサードパーティのアドインが実行されていると、GC.WaitForPendingFinalizersが永久にハングします。 、そのため、そのサードパーティに「悪い」ファイナライザーが追加されているのではないかと思います。私はその追加(クライアントの要件)を置き換える/削除することを制御できないので、それらを共存させる方法を自分で理解する必要があります。

4

1 に答える 1

8

とにかく、現時点では、プロセス内のCLRインスタンスごとに1つのスレッドにすぎないように見えます。これを示すコードを次に示します。

Test.cs:

using System;

class Test
{
    static void Main()
    {
        AppDomain.CreateDomain("First")
                 .ExecuteAssembly("ShowFinalizerThread.exe");
        AppDomain.CreateDomain("Second")
                 .ExecuteAssembly("ShowFinalizerThread.exe");
    }
}

ShowFinalizerThread.cs:

using System;
using System.Threading;

class ShowFinalizerThread
{
    static Random rng = new Random();

    ~ShowFinalizerThread()
    {
        Console.WriteLine("Thread/domain: {0}/{1}",
                          Thread.CurrentThread.ManagedThreadId,
                          AppDomain.CurrentDomain.FriendlyName);
        if (rng.Next(10) == 0)
        {
            Console.WriteLine("Hanging!");
            Thread.Sleep(2000);
        }
    }

    static void Main()
    {
        new Thread(LoopForever).Start();
    }

    static void LoopForever()
    {
        while (true)
        {
            new ShowFinalizerThread();
            GC.Collect();
            GC.WaitForPendingFinalizers();
            Thread.Sleep(300);
        };
    }
}

それぞれをコンソール アプリとしてコンパイルし、test.exe を実行します (コマンド ラインから実行するのが最も簡単です。IMO)。あるアプリ ドメインのファイナライザーが別のアプリ ドメインをブロックしていることがわかります。

将来、AppDomain ごとではなく、コアごとに 1 つのファイナライザー スレッドが表示されても驚かないでしょうが、それでも問題が発生するようです :(

あなたは私の最も深い同情を持っています (解決策ではありませんが) - Oracle Blob のデッドロックを突き止めたとき. 適切に処分することでそれを修正することができましたが、すべてがうまく機能するわけではないことを私は知っています。

于 2008-10-27T23:02:01.830 に答える