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

2023-09-04 00:12:59 作者:孤独患者

我有一个加载外部组件,我无法控制(类似于一个插件模型,其他人创造和发展了由主应用程序使用的组件)的应用程序。它通过对这些组件,然后在装配完成后使用,主要的应用程序域卸载它们创造新的AppDomain中加载它们。

目前,它简单化unloads这些组件通过

 尝试
{
    AppDomain.Unload(otherAssemblyDomain);
}
赶上(例外的例外)
{
    //日志异常
}
 

但是,有时,异常在卸货过程中引发了专门CannotUnloadAppDomainException.据我了解,这可能因为在孩子的AppDomain的   微信群聊记录删了后别人还能看到吗

当一个线程调用卸​​载,目标   域标记卸货。该   专用线程尝试卸载   域,并且在所有线程   域被中止。如果一个线程执行   不中止,例如,因为它是   执行非托管code,或者是因为   它在执行finally块,然后   一段时间后,一   CannotUnloadAppDomainException是   扔在线程最初   所谓卸载。如果该线程   不能被最终中止结束,   目标域不卸载。   因此,在.NET Framework版本   2.0域是不能保证卸载,因为它可能不   可能终止执行   线程。

我担心的是,如果没有加载程序集,那么它可能会导致内存泄漏。一个潜在的解决办法是杀死主应用程序本身如果发生上述异常,但我宁愿避免这种激烈的行动。

我还在考虑重复装卸要求一些额外的尝试。或许,这样的约束的环路:

 尝试
{
    AppDomain.Unload(otherAssemblyDomain);
}
赶上(CannotUnloadAppDomainException除外)
{
    //日志异常
    变种I = 0;
    而(我3;)//三次尝试后退出
    {
        Thread.sleep代码(3000); //等待几秒钟,然后重试...
        尝试
        {
            AppDomain.Unload(otherAssemblyDomain);
        }
        赶上(例外)
        {
            //日志异常
            我++;
            继续;
        }
        打破;
    }
}
 

这是否有道理?应我甚至有想再次卸载烦?如果我只是尝试了一次,继续前进?有没有别的东西,我应该怎么办?此外,有没有什么可以从主要的AppDomain来控制外部组件来完成,如果线程仍在运行(记住别人编写和运行这个外部code)?

我想了解什么是最佳实践管理多个AppDomain的时候。

解决方案

我已经经历了我的应用程序类似的问题。基本上,你不能做任何事情更给力的AppDomain 下井比卸载一样。

它基本上呼吁中止正在执行的code。在的AppDomain 所有线程的,并且如果code卡在终结或非托管code,没有太多可以做

如果,基于问题的程序,很可能是终结/非托管code将结束了一段时间后,你可以绝对卸载再打电话。如果没有,你可以对泄漏的目的或循环过程中的域。

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.