System.CannotUnloadAppDomainException
アプリケーション ドメインをアンロードしようとして失敗した場合にスローされる例外。
二重に Unload しようとした場合も発生するようだが、そちらは単純なので置いておく。
今回は、子プログラムが終了に「もたつく」ケースを実験。
[実験ソース(C#)]
まずは、親プログラム。
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
AppDomain domain = AppDomain.CreateDomain("MyDomain");
string path = Path.GetFullPath(@"../../../ChildApp/bin/Release/ChildApp.exe");
Task[] tasks = new Task[2];
tasks[0] = Task.Factory.StartNew(() => domain.ExecuteAssembly(path, new string[] { "3", "aaa" }));
tasks[1] = Task.Factory.StartNew(() => domain.ExecuteAssembly(path, new string[] { "5", "bbb" }));
Task.WaitAny(tasks);
// どれか1つでもタスクが終了すれば、強制終了する。
Console.WriteLine("unloaded");
AppDomain.Unload(domain);
}
}
}
続いて、ドメインに含まれる子プログラム。
using System;
using System.Threading;
namespace ChildApp
{
class Program
{
/// <summary>
/// 1秒毎に表示するアプリ
/// </summary>
/// <param name="args">カウント, 文字</param>
static void Main(string[] args)
{
int cnt = int.Parse(args[0]); // エラー処理は省略
string msg = args[1];
try
{
for (int i = 0; i < cnt; i++)
{
Thread.Sleep(1000);
Console.WriteLine(msg);
}
}
catch (Exception e)
{
Console.Write(string.Format("プロセス({0})の中断:", msg));
Console.WriteLine(e.Message);
}
finally
{
// Domainがunloadされると、強制終了になるが、
// すぐには停止できない状況を作り出す。
Thread.Sleep(2000);
}
}
}
}
[結果]
bbb
aaa
bbb
aaa
bbb
aaa
bbb
bbb
unloadedハンドルされていない例外: System.CannotUnloadAppDomainException: appdomain をアンロード中にエラーが発生しました。 (HRESULT からの例外: 0x80131015)
場所 System.AppDomain.Unload(AppDomain domain)
場所 ConsoleApplication1.Program.Main(String[] args) 場所 Program.cs:行 23
[対処など]
ドメインのアンロード処理は非常に重い処理のようで、予定では3秒後(3回繰り返した後)、unloaded と表示されるはずが、5秒かかっても終わらないため、2つ目の子プロセス(5回繰り返す方)も正常に完了してしまう。
子プロセスが簡単に終了すれば、どうか? ということで、finally ブロックで2秒待っているところを、1秒に書き換えて実行した結果が次。
[結果]
aaa
bbb
aaa
bbb
aaa
bbb
bbb
unloaded
プロセス(bbb)の中断:スレッドを中止しようとしました。
この場合、期待通りドメインを Unload することで子スレッドは、アボートする。
実際に使用する局面では、どのように待ち合わせすればよいか、非常に難しい問題だと感じた。