如何使用C#正确卸载AppDomain?
我有一个应用程序加载我无法控制的外部程序集(类似于其他人创建和开发主应用程序使用的程序集的插件模型)。 它通过为这些程序集创建新的AppDomain来加载它们,然后在使用程序集完成后,主AppDomain将卸载它们。
目前,它简单地卸载这些组件
try { AppDomain.Unload(otherAssemblyDomain); } catch(Exception exception) { // log exception }
但是,有时会在卸载过程中抛出exception,特别是CannotUnloadAppDomainException
。 根据我的理解,这是可以预料到的,因为由于非托管代码仍在执行或线程在finally
块中的情况,子域AppDomains中的线程无法强制中止 :
当线程调用Unload时,目标域将标记为卸载。 专用线程尝试卸载域,并且域中的所有线程都将中止。 如果线程没有中止,例如因为它正在执行非托管代码,或者因为它正在执行finally块,那么在一段时间之后,在最初调用Unload的线程中抛出CannotUnloadAppDomainException。 如果最终无法中止的线程结束,则不会卸载目标域。 因此,在.NET Framework 2.0版域中不保证卸载,因为它可能无法终止执行线程。
我担心的是,如果没有加载程序集,那么它可能会导致内存泄漏。 如果出现上述exception,可能的解决方案是杀死主应用程序进程,但我宁愿避免这种激烈的行为。
我还在考虑重复卸载电话以进行一些额外的尝试。 也许像这样的约束循环:
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时的最佳做法。
我在我的应用程序中遇到了类似的问题。 基本上,你不能做任何更多的事情来迫使AppDomain
失败,而不是Unload
。
它基本上调用了在AppDomain
中执行代码的所有线程的中止,如果该代码卡在终结器或非托管代码中,则没有太多可以完成的事情。
如果根据所讨论的程序,终结器/非托管代码可能会在稍后完成,您绝对可以再次调用Unload
。 如果没有,您可以故意泄漏域或循环该过程。
如果不卸载域,请尝试生成GC.Collect()。
try { AppDomain.Unload(otherAssemblyDomain); } catch (CannotUnloadAppDomainException) { GC.Collect(); AppDomain.Unload(otherAssemblyDomain); }