如何使用 C# 正确卸载 AppDomain? [英] How to properly unload an AppDomain using C#?

查看:57
本文介绍了如何使用 C# 正确卸载 AppDomain?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个应用程序可以加载我无法控制的外部程序集(类似于其他人创建和开发主应用程序使用的程序集的插件模型).它通过为这些程序集创建新的 AppDomain 来加载它们,然后在使用完这些程序集后,主 AppDomain 卸载它们.

目前,它简单地通过

卸载这些程序集

试试{AppDomain.Unload(otherAssemblyDomain);}捕获(异常异常){//记录异常}

但是,有时会在卸载过程中抛出异常,具体<代码>CannotUnloadAppDomainException.据我了解,这是可以预料的,因为子 AppDomains 不能因为非托管代码仍在执行或线程处于finally的情况而被强制中止:

<块引用>

当一个线程调用 Unload 时,目标域被标记为卸载.这专用线程尝试卸载域,以及域中的所有线程域中止.如果一个线程不中止,例如因为它是执行非托管代码,或者因为它正在执行一个 finally 块,然后一段时间后无法UnloadAppDomainException 是扔在原来的线程中称为卸载.如果那个线程无法中止最终结束,目标域未卸载.因此,在 .NET Framework 版本中2.0 域不能保证卸载,因为它可能不会可以终止执行线程.

我担心的是,如果未加载程序集,则可能会导致内存泄漏.如果发生上述异常,一个潜在的解决方案是终止主应用程序进程本身,但我宁愿避免这种激烈的操作.

我还考虑重复卸载调用以进行一些额外的尝试.也许像这样的约束循环:

试试{AppDomain.Unload(otherAssemblyDomain);}catch (CannotUnloadAppDomainException 异常){//记录异常变量 i = 0;while (i < 3)//尝试三次后退出{线程.睡眠(3000);//等待几秒钟再试一次...尝试{AppDomain.Unload(otherAssemblyDomain);}捕获(异常){//记录异常我++;继续;}休息;}}

这有意义吗?我什至应该费心尝试再次卸载吗?我应该尝试一次然后继续吗?还有什么我应该做的吗?另外,如果线程仍在运行,是否可以从主 AppDomain 执行任何操作来控制外部程序集(请记住其他人正在编写和运行此外部代码)?

我正在尝试了解管理多个 AppDomain 时的最佳做法.

解决方案

我在我的应用中处理过类似的问题.基本上,与 Unload 相比,您无法做更多的事情来强制 AppDomain 关闭.

它基本上会调用在 AppDomain 中执行代码的所有线程的中止,如果该代码卡在终结器或非托管代码中,则无能为力.>

如果根据所讨论的程序,终结器/非托管代码可能会在稍后完成,您绝对可以再次调用 Unload.如果没有,您可以故意泄漏域或循环该过程.

I have an application that loads external assemblies which I have no control over (similar to a plugin model where other people create and develop assemblies that are used by the main application). It loads them by creating new AppDomains for these assemblies and then when the assemblies are done being used, the main AppDomain unloads them.

Currently, it simplistically unloads these assemblies by

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

However, on occasion, exceptions are thrown during the unloading process specifically CannotUnloadAppDomainException. From what I understand, this can be expected since a thread in the children AppDomains cannot be forcibly aborted due to situations where unmanaged code is still being executed or the thread is in a finally block:

When a thread calls Unload, the target domain is marked for unloading. The dedicated thread attempts to unload the domain, and all threads in the domain are aborted. If a thread does not abort, for example because it is executing unmanaged code, or because it is executing a finally block, then after a period of time a CannotUnloadAppDomainException is thrown in the thread that originally called Unload. If the thread that could not be aborted eventually ends, the target domain is not unloaded. Thus, in the .NET Framework version 2.0 domain is not guaranteed to unload, because it might not be possible to terminate executing threads.

My concern is that if the assembly is not loaded, then it could cause a memory leak. A potential solution would be to kill the main application process itself if the above exception occurs but I rather avoid this drastic action.

I was also considering repeating the unloading call for a few additional attempts. Perhaps a constrained loop like this:

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;
    }
}

Does this make sense? Should I even bother with trying to unload again? Should I just try it once and move on? Is there something else I should do? Also, is there anything that can be done from the main AppDomain to control the external assembly if threads are still running (keep in mind others are writing and running this external code)?

I'm trying understand what are best practices when managing multiple AppDomains.

解决方案

I've dealt with a similar problem in my app. Basically, you can't do anything more to force the AppDomain to go down than Unload does.

It basically calls abort of all threads that are executing code in the AppDomain, and if that code is stuck in a finalizer or unmanaged code, there isn't much that can be done.

If, based on the program in question, it's likely that the finalizer/unmanaged code will finish some later time, you can absolutely call Unload again. If not, you can either leak the domain on purpose or cycle the process.

这篇关于如何使用 C# 正确卸载 AppDomain?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆