如何使用C#正确卸载AppDomain?

时间:2010-12-13 18:24:32

标签: c# .net appdomain

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

目前,它通过

简化unloads这些程序集
try
{
    AppDomain.Unload(otherAssemblyDomain);
}
catch(Exception exception)
{
    // log exception
}

但是,有时会在卸载过程中抛出异常CannotUnloadAppDomainException。根据我的理解,这可以预期,因为孩子AppDomains cannot be forcibly aborted due to situations where unmanaged code is still being executed or the thread is in a finally block中的一个主题:

  

当线程调用Unload时,目标   域标记为卸载。该   专用线程试图卸载   域,以及中的所有线程   域被中止。如果一个线程   不要中止,例如因为它   执行非托管代码,或者因为   它正在执行finally块,然后   经过一段时间后   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时的最佳做法。

2 个答案:

答案 0 :(得分:10)

我在我的应用中处理过类似的问题。基本上,你不能做任何更多的事情来迫使AppDomain下降而不是Unload

它基本上调用了在AppDomain中执行代码的所有线程的中止,如果该代码卡在终结器或非托管代码中,则没有太多可以做的。

如果根据相关程序,终结器/非托管代码可能会在稍后完成,您绝对可以再次呼叫Unload。如果没有,您可以故意泄漏域或循环该过程。

答案 1 :(得分:0)

如果您不卸载域,请尝试制作GC.Collect()。

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