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

csga3l58  于 2023-03-04  发布在  .NET
关注(0)|答案(3)|浏览(185)

我有一个加载外部程序集的应用程序,我无法控制这些程序集(类似于其他人创建和开发主应用程序使用的程序集的插件模型)。它通过为这些程序集创建新的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时的最佳做法。

xwbd5t1u

xwbd5t1u1#

我在我的应用程序中处理过类似的问题,基本上,你不能做任何比Unload更多的事情来迫使AppDomain关闭。
它基本上调用了正在AppDomain中执行代码的所有线程的abort,如果该代码停留在终结器或非托管代码中,则没有什么可做的。
如果根据所讨论的程序,终结器/非托管代码可能会在稍后完成,则完全可以再次调用Unload;如果不是,则可以故意泄漏域或循环该进程。

gblwokeq

gblwokeq2#

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

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

chhqkbe13#

我有类似的问题与随机行为几个月了,现在,(与一些应用程序。卸载甚至阻止永远!在一些机器上)终于决定采取一个大呼吸,使进程隔离。你可以产生子控制台进程和重定向输出
如果你需要取消,这是在鼻子上的手指杀死子进程和所有的依赖/句柄。
在极端情况下,我不得不运行专门的清理代码,并来到解决方案,以创建额外的进程与专用的cmd行等待输入提取的控制台输出的初始运行程序进程。
是的,这个应用程序域是一个真实的的笑话,我认为这不是一个巧合,它不再是在net核心。

相关问题