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

发布于 2024-10-07 02:41:00 字数 1871 浏览 0 评论 0原文

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

目前,它简单地卸载 这些程序集但是

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

,有时,在卸载过程中会抛出异常,特别是 CannotUnloadAppDomainException。据我了解,这是可以预料的,因为子 AppDomain 中的线程 由于非托管代码仍在执行或线程位于 finally 块中的情况,无法强制中止

当线程调用 Unload 时,目标 域被标记为卸载。这 专用线程尝试卸载 域以及域中的所有线程 域已中止。如果一个线程这样做 不中止,例如因为它是 执行非托管代码,或者因为 它正在执行一个finally块,然后 一段时间后a 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 是否可以执行任何操作来控制外部程序集(请记住其他人正在编写和运行此外部代码)?

我试图了解管理多个应用程序域时的最佳实践是什么。

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

心房的律动 2024-10-14 02:41:00

我在我的应用程序中处理过类似的问题。基本上,您无法执行任何比 Unload 更强制 AppDomain 关闭的操作。

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

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

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.

如歌彻婉言 2024-10-14 02:41:00

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

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

Try to make GC.Collect() if you do not unload the domain.

 try
    {
       AppDomain.Unload(otherAssemblyDomain);
    }
    catch (CannotUnloadAppDomainException)
    {
       GC.Collect();
       AppDomain.Unload(otherAssemblyDomain);
    }
兔姬 2024-10-14 02:41:00

几个月来我遇到了类似的随机行为问题,(在某些机器上,某些应用程序甚至永远阻塞!)
最后决定深吸一口气,进行进程隔离。
您可以生成子控制台进程并重定向输出

如果您需要取消此操作以杀死子进程和所有依赖项/句柄,

。在极端情况下,我必须运行专用的清理代码,并找到解决方案来创建附加进程,并使用从初始运行程序进程的控制台输出中提取的专用命令行等待输入。

是的,这个应用程序域是一个真正的笑话,我认为它不再位于网络核心中并不是巧合。

I had similar issues with random behavior for months now, (with some app.Unload even BLOCKING forever ! on some machines)
finally decided to take a big breath and made process isolation.
you can spawn child console process and redirect output

if you need to cancel this is finger in the nose to kill child process and all dependencies / handles.

To an extreme i had to run dedicated cleanup code, and came to solution to create additional process with dedicated cmd line waiting input extracted from console output of initial runner process.

yes this app domain is a real joke and i think this is not a coincidence that it is not anymore in net core.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文