为啥子 AppDomain 中的 ***Exception 会终止父 AppDomain?

Posted

技术标签:

【中文标题】为啥子 AppDomain 中的 ***Exception 会终止父 AppDomain?【英文标题】:Why does a ***Exception in a child AppDomain terminate the parent AppDomain?为什么子 AppDomain 中的 ***Exception 会终止父 AppDomain? 【发布时间】:2011-02-22 12:38:52 【问题描述】:

我的印象是 AppDomain 是相互隔离的。似乎在 StackOverException 的情况下,情况并非如此。

为了演示这个问题,我创建了一个简单的控制台应用程序,其唯一目的是生成一个新的 AppDomain,我在其中加载了一个非常简单的程序集并调用其中一个方法。此方法恰好抛出 ***Exception。这会导致我的控制台应用程序毫不客气地终止。

我想要的行为是让“子”AppDomain 在此类异常上崩溃并烧毁,但让我的控制台应用程序在“父”AppDomain 中运行,毫发无损。

这可能吗?

更新:这是一些代码。两个异常处理程序均未命中。

    class Program
    
        static void Main(string[] args)
        
            AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;

            // create app domain
            var domain = AppDomain.CreateDomain("MyDomain");

            // create a component
            var component = (MyComponent)domain.CreateInstanceAndUnwrap(
                "AppDomainMonitor.Component", 
                typeof(MyComponent).FullName);

            // create a thread from a method on this component
            var thread = new Thread(component.Cause***);

            // start the thread
            thread.Start();

            Console.ReadKey();
        

        static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
        
            // never hit
        
    

    public class MyComponent : MarshalByRefObject
    
        public void Cause***()
        
            try
            
                Infinite();
            
            catch (Exception ex)
            
                // never hit
            
        


        void Infinite()
        
            Infinite();
        
    

【问题讨论】:

能看到代码示例吗? :) 我过去设置了类似的东西,它经受住了异常(尽管我不记得尝试过 ***Exception)。我很想看看是否有一些古怪的东西导致程序集被加载到您的默认应用程序域中。干杯! 【参考方案1】:

AppDomain 之间仅隔离托管内存。如果一个线程在任何 AppDomain 中抛出异常,这将导致整个应用程序崩溃。

我认为最好的解决方案是确保在每个线程(或线程池工作项)上正确处理所有异常。

然而,有一个 hack 包括在 App.Config 文件中应用此配置:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <runtime>
    <legacyUnhandledExceptionPolicy enabled="1"/>
  </runtime>
</configuration>

自 .Net 2.0 以来,未处理的异常会导致进程崩溃,但您可以使用此技巧恢复到旧策略。

我会谨慎使用这种方法,因为通常最好让进程崩溃而不是静默失败。您可以在 AppDomain.UnhandledException 中添加跟踪,以便在发生未处理的异常时得到通知并适当处理。

编辑

您对 StackOveflowException 的看法是正确的,因为 .Net 2.0 无法由用户代码处理此异常。 (见this page in msdn的备注部分)。

有一种方法可以通过创建自定义 CLR 主机来覆盖它,但这似乎是一件疯狂的事情。我想你必须忍受它,或者如果你真的需要这种容错能力,你可以创建子进程而不是 AppDomain。

【讨论】:

感谢您的回复。我在上面的问题中添加了一些代码,但我没有看到任何异常处理程序被命中,所以我不确定如何“正确”处理它们...... 强烈不建议打开 legaceUnhandledExceptionPolicy,因为这就像在说“嘿,我不知道发生了什么以及如何解决它,停止抛出这个该死的异常”。这就像一个空的“抓住一切”块。我想这种可能性是为了与遗留代码兼容,它不打算与新代码一起使用。【参考方案2】:

我想问题是你在主 AppDomain 中调用了导致异常的方法,也就是说你在主 AppDomain 中调用了某个方法,这个方法从子 AppDomain 调用了问题方法。子 AppDomain 中出现异常,但它会沿调用堆栈向上传播到您的调用 AppDomain(主 AppDomain)。尝试完全从子 AppDomain 调用此方法。例如,在子 AppDomain 中生成一个线程,以便该线程应该调用问题方法。

【讨论】:

即使您没有使用单独的应用程序域,生成线程也会“隐藏”异常。 没有必要。您可以正确处理该线程内的异常。 @DmitryLobanov 启发我们如何做?

以上是关于为啥子 AppDomain 中的 ***Exception 会终止父 AppDomain?的主要内容,如果未能解决你的问题,请参考以下文章

为啥子查询中的 distinct on 会损害 PostgreSQL 的性能?

为啥子进程中的管道有时会中断,有时不会?

为啥子查询解决方法中的这个 ORDER BY 不能始终如一地工作?

为啥子控件不出现在 MFC 派生的 CWnd 中

为啥子类继承私有字段?

为啥子视图控制器占据整个屏幕?