web-dev-qa-db-ja.com

C#を使用してAppDomainを適切にアンロードする方法は?

制御できない外部アセンブリをロードするアプリケーションがあります(他の人がメインアプリケーションで使用されるアセンブリを作成および開発するプラグインモデルに似ています)。これらのアセンブリの新しいAppDomainを作成してロードし、アセンブリの使用が完了すると、メインのAppDomainがそれらをアンロードします。

現在、単純に nloads これらのアセンブリは

try
{
    AppDomain.Unload(otherAssemblyDomain);
}
catch(Exception exception)
{
    // log exception
}

ただし、場合によっては、特にアンロードプロセス中に例外がスローされます CannotUnloadAppDomainException 。私の理解では、子AppDomainsのスレッド アンマネージコードがまだ実行されているか、スレッドがfinallyブロックにあるため、強制的に中止できないため

スレッドがUnloadを呼び出すと、ターゲットドメインはアンロード用にマークされます。専用スレッドがドメインのアンロードを試み、ドメイン内のすべてのスレッドが中止されます。アンマネージコードを実行している、またはfinallyブロックを実行しているなどの理由でスレッドが異常終了しない場合、一定期間後、元々Unloadを呼び出したスレッドでCannotUnloadAppDomainExceptionがスローされます。中止できなかったスレッドが最終的に終了した場合、ターゲットドメインはアンロードされません。したがって、.NET Frameworkバージョン2.0では、実行中のスレッドを終了できない可能性があるため、ドメインのアンロードは保証されません。

私の懸念は、アセンブリがロードされていない場合、メモリリークが発生する可能性があることです。考えられる解決策は、上記の例外が発生した場合にメインアプリケーションプロセス自体を強制終了することですが、私はむしろこの抜本的なアクションを回避します。

また、アンロードの呼び出しを数回繰り返すことも検討していました。おそらく、次のような制約付きループです。

try
{
    AppDomain.Unload(otherAssemblyDomain);
}
catch (CannotUnloadAppDomainException exception)
{
    // log exception
    var i = 0;
    while (i < 3)   // quit after three tries
    {
        Thread.Sleep(3000);     // wait a few secs before trying again...
        try
        {
            AppDomain.Unload(otherAssemblyDomain);
        }
        catch (Exception)
        {
            // log exception
            i++;
            continue;
        }
        break;
    }
}

これは意味がありますか?もう一度アンロードしようと気にする必要がありますか?一度試して先に進むべきですか?他にやるべきことはありますか?また、スレッドがまだ実行されている場合に、メインのAppDomainから外部アセンブリを制御するために実行できることはありますか(他の人がこの外部コードを記述して実行していることに注意してください)。

複数のAppDomainを管理する際のベストプラクティスを理解しようとしています。

22
Ray

私は自分のアプリで同様の問題に対処しました。基本的に、AppDomainを強制的にダウンさせるためにUnloadよりも多くのことを行うことはできません。

基本的には、AppDomainでコードを実行しているすべてのスレッドの中止を呼び出します。そのコードがファイナライザーまたはアンマネージコードでスタックしている場合、実行できることはほとんどありません。

問題のプログラムに基づいて、ファイナライザー/アンマネージコードが後で終了する可能性が高い場合は、絶対にUnloadを再度呼び出すことができます。そうでない場合は、意図的にドメインをリークするか、プロセスを循環させることができます。

11
aL3891

ドメインをアンロードしない場合は、GC.Collect()を作成してみてください。

 try
    {
       AppDomain.Unload(otherAssemblyDomain);
    }
    catch (CannotUnloadAppDomainException)
    {
       GC.Collect();
       AppDomain.Unload(otherAssemblyDomain);
    }
0