【问题标题】:How to gracefully unload a child AppDomain that has threads running如何优雅地卸载运行线程的子 AppDomain
【发布时间】:2011-03-30 23:55:57
【问题描述】:

我有一个服务,它加载一个子 AppDomain,然后启动一个在其中运行的线程。它需要一个 AppDomain,因为它动态生成和加载一些代码,我需要能够在不终止整个服务的情况下重新启动它。

所以有一个线程在子 AppDomain 的事件循环中运行,它通过 MarshalByRefObject 将事件传递给它,该对象将内容粘贴在并发队列中。我想停止并卸载子 AppDomain 并创建一个新的。

我可以简单地在子 AppDomain 上调用 Unload,但这会中止所有线程并抛出 ThrearAbortException。我怎样才能优雅地关闭它?如果我使用 MarshalByRefObject 在子 AppDomain 中设置一些静态标志,那么主进程如何能够等到它完成卸载?

我有一些示例代码显示了它的设置方式以及我如何调用 Unload 来杀死它,我如何修改它以允许优雅卸载并且永远不会有多个子 AppDomain?

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security;
using System.Security.Permissions;
using System.Reflection;
using System.Threading;

namespace TestAppDomains
{
    /// <summary>
    /// Calls to methods magically get transfered to the appdomain it was created in because it derives from MarshalByRefObject
    /// </summary>
    class MarshalProxy : MarshalByRefObject
    {
        public AppDomain GetProxyAppDomain()
        {
            return AppDomain.CurrentDomain;
        }

        public void SayHello()
        {
            Console.WriteLine("MarshalProxy in AD: {0}", AppDomain.CurrentDomain.FriendlyName);
        }

        public void RunLoop()
        {
            try
            {
                while (true)
                {
                    Console.WriteLine("RunLoop {0} in {1}", DateTime.Now.ToLongTimeString(), AppDomain.CurrentDomain.FriendlyName);
                    Thread.Sleep(1000);
                }
            }
            catch(Exception ex)
            {
                Console.WriteLine("You killed me! {0}", ex);
                Thread.Sleep(200); //just to make sure the unload is really blocking until its done unloading
                // if the sleep is set to 2000 then you will get a CannotUnloadAppDomainException, Error while unloading appdomain. (Exception from HRESULT: 0x80131015) thrown from the .Unload call
            }
        }

        static int creationCount = 1;
        public static MarshalProxy RunInNewthreadAndAppDomain()
        {
            // Create the AppDomain and MarshalByRefObject
            var appDomainSetup = new AppDomainSetup()
            {
                ApplicationName = "Child AD",
                ShadowCopyFiles = "false",
                ApplicationBase = Environment.CurrentDirectory,
            };

            var childAppDomain = AppDomain.CreateDomain(
                "Child AD " + creationCount++,
                null,
                appDomainSetup,
                new PermissionSet(PermissionState.Unrestricted));

            var proxy = (MarshalProxy)childAppDomain.CreateInstanceAndUnwrap(
                typeof(MarshalProxy).Assembly.FullName,
                typeof(MarshalProxy).FullName,
                false,
                BindingFlags.Public | BindingFlags.Instance,
                null,
                new object[] { },
                null,
                null);

            Thread runnerThread = new Thread(proxy.RunLoop);
            runnerThread.Name = "MarshalProxy RunLoop";
            runnerThread.IsBackground = false;
            runnerThread.Start();

            return proxy;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("I am running in AD: {0}", AppDomain.CurrentDomain.FriendlyName);

            var proxy = MarshalProxy.RunInNewthreadAndAppDomain();
            proxy.SayHello();

            while (true)
            {
                Console.WriteLine("Press enter to kill and restart proxy");
                Console.WriteLine();
                Console.ReadLine();

                Console.WriteLine("Unloading");
                AppDomain.Unload(proxy.GetProxyAppDomain());
                Console.WriteLine("Done unloading");

                proxy = MarshalProxy.RunInNewthreadAndAppDomain();
            }
        }
    }
}

【问题讨论】:

  • 先优雅地停止线程。

标签: c# concurrency marshalling appdomain


【解决方案1】:

试试下面的

runnerThread.IsBackground = true;

而且,是的,如果您没有先停止线程,则不会优雅地卸载 AppDomain。

【讨论】:

    【解决方案2】:

    这种情况本质上就像两个 AppDomain 是单独的进程一样,因此您需要使用某种形式的 IPC。一种选择是在要求循环停止时将事件句柄传递给子 AppDomain。循环可以在退出之前发出事件信号。等待事件给循环一些时间来完成。如果超时,则可以进行粗略卸载。

    【讨论】:

      【解决方案3】:

      序列化所有子 AppDomain。将其发送到远程服务器。关闭该服务器。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-11-06
        • 2011-03-12
        • 2015-06-07
        • 1970-01-01
        • 2010-09-12
        相关资源
        最近更新 更多