【问题标题】:UnhandledException not called when exception thrown in another thread在另一个线程中抛出异常时未调用 UnhandledException
【发布时间】:2011-11-14 18:46:09
【问题描述】:

根据 Microsoft 文档,当线程(来自线程池或使用 System.Threading.Thread 类创建)上发生未处理的异常时,应为应用程序的默认 AppDomain 触发 AppDomain.UnhandledException 事件。这是 MSDN link 在第二个 NOTE 部分之后解释它。

但我无法重现此行为,据我从测试应用程序中得知,它永远不会在默认 AppDomain 或用于创建线程的 AppDomain 上触发 UnhandledException。是文档错误还是我的测试代码错误?

using System;
using System.Runtime.ExceptionServices;
using System.Reflection;

public class Program
{
    static void Main()
    {
        Program.HookAppDomainExceptions();
        Test t = CreateTestInsideAppDomain("Nested1");
        t.SetupNested1();
        Console.ReadLine();
    }

    public static Test CreateTestInsideAppDomain(string appDomainName)
    {
        AppDomain nested1 = AppDomain.CreateDomain(appDomainName);
        string executingName = Assembly.GetExecutingAssembly().FullName;
        return (Test)nested1.CreateInstanceAndUnwrap(executingName, "Test");
    }

    public static void HookAppDomainExceptions()
    {
        AppDomain.CurrentDomain.FirstChanceException +=
            new EventHandler<FirstChanceExceptionEventArgs>(FirstChanceException);

        AppDomain.CurrentDomain.UnhandledException +=
            new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
    }

    public static void FirstChanceException(object sender, FirstChanceExceptionEventArgs e)
    {
        Console.WriteLine("Domain:{0} FirstChanceException Handler",
                          AppDomain.CurrentDomain.FriendlyName);
    }

    public static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
    {
        Console.WriteLine("Domain:{0} UnhandledException Handler",
                          AppDomain.CurrentDomain.FriendlyName);
    }
}

public class Test : MarshalByRefObject
{
    private delegate void Nothing();

    public void SetupNested1()
    {
        var start = new Nothing(Nested1ThreadStart);
        start.BeginInvoke(null, null);
    }

    static void Nested1ThreadStart()
    {
        Program.HookAppDomainExceptions();
        Test t = Program.CreateTestInsideAppDomain("Nested2");
        t.SetupNested2();
    }

    public void SetupNested2()
    {
        Program.HookAppDomainExceptions();
        Test t = Program.CreateTestInsideAppDomain("Nested3");
        t.ThrowException();
    }

    public void ThrowException()
    {
        Program.HookAppDomainExceptions();
        throw new ApplicationException("Raise Exception");
    }
}

【问题讨论】:

    标签: c# exception .net-4.0 clr


    【解决方案1】:

    在您的代码中,UnhandledException 不会在任何 AppDomain 上触发,因为如果您使用 BeginInvoke() 调用委托,则在其执行期间抛出的任何异常都会被处理,然后在您调用 EndInvoke() 时重新抛出,你没有。

    如果您拨打EndInvoke()

    start.EndInvoke(start.BeginInvoke(null, null));
    

    或同步执行委托:

    start();
    

    你会得到类似的结果:UnhandledException 的主域被提升。

    如果相反,您按照文档中的说明执行操作并使用 Thread 类启动一个新线程:

    new Thread(Nested1ThreadStart).Start();
    

    UnhandledExceptionNested1 和主应用程序域被提升。

    所以,回答您的问题:文档是正确的。你的代码是错误的。当您使用BeginInvoke() 异步调用委托时,您应该总是稍后再调用EndInvoke()

    【讨论】:

    • 我宁愿选择start.BeginInvoke(iar =&gt; start.EndInvoke(iar), null),因为您的使用使方法调用同步。否则,很好的答案!
    • @Jonathan,是的,这也有效,但它改变了行为。它在用于调用start 的线程上调用EndInvoke(),这意味着在Nested1(和主域)上引发UnhandledException
    • @svick - EndInvoke(BeginInvoke) 没有意义,除非创建一个自然异步的方法的同步版本。
    • @Jonathan,在实际应用中,没有。但这不是实际的应用程序,它只是用于确定UnhandledException 行为方式的代码。
    • 优秀的答案,正是我想要的!谢谢。
    【解决方案2】:

    我也有这个问题。我使用 观察者模式 来解决这个问题。 您可以在调用者类中实现一个接口,该接口具有一个在发生异常时从另一个线程调用的方法。

    这里有一个链接显示如何实现这个模式Exploring the Observer Design Pattern

    【讨论】:

    • 这不是这个问题的目的。如果他想要(或能够)这样做,OP(可能)知道如何处理异常。问题是未处理的异常会发生什么。
    猜你喜欢
    • 2016-07-28
    • 2012-01-11
    • 2021-09-16
    • 2022-01-09
    • 2015-02-23
    • 2022-01-18
    • 2022-09-28
    • 2011-06-25
    • 2020-11-30
    相关资源
    最近更新 更多