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 することで子スレッドは、アボートする。
実際に使用する局面では、どのように待ち合わせすればよいか、非常に難しい問題だと感じた。