为什么在ThreadPool线程上引发的异常不会被C#中的主线程处理

时间:2011-04-19 09:49:38

标签: c#

我有以下代码片段。

class Program
{
    static void Main(string[] args)
    {
        try
        {
            Thread m_thread = new Thread(() =>
                {
                    //try
                    //{
                        checkexc();
                    //}
                    //catch (Exception ex)
                    //{

                    //}
                }
                );               
            m_thread.Start();

        }
        catch (Exception ex)
        {

        }
    }
    static void checkexc()
    {
        throw new NullReferenceException();
    }
}

覆盖Try-Catch块不处理NullReferenceException。但是,如果我将委托包装在thread()构造函数中,那么它将由Try-Catch处理。为什么外部Try-Catch不处理此异常。

6 个答案:

答案 0 :(得分:7)

想象一下,你有一条主要道路(A),另一条道路从该道路分支出来(B)。

当卡车沿着A行驶时,如果它发生碰撞,那么A会知道它,并且交通将会停止。

当卡车驶入B时,如果卡车崩溃,那么B将知道它,并且交通将停止。

但B会告诉A一辆卡车撞到它的机制是什么?

一旦卡车在B上,它不会影响A,除非A到B有另一个入口点。

如何将其他线程中的异常传递给主线程?一旦另一个线程运行,它就不再(直接)与主线程通信。

答案 1 :(得分:1)

因为异常没有在线程构造函数中发生。只有在调用m_thread.Start()并在另一个线程上执行后才调用checkexc()。

答案 2 :(得分:1)

从另一个角度回答这个问题:

任务并行库(TPL)为您处理(捕获和传播)异常

但是,例外情况出现在Wait()上,以完成任务,而不是在启动任务的点/线程上。

    // No point in specifically surrounding the next 2 statements
    Task t1 = Task.Factory.StartNew(() => Foo());
    Task t2 = Task.Factory.StartNew(() => Bar());

    try
    {
        Task.WaitAll(t1, t2);
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex);  // Aggregated exceptions from Foo and/or Bar
    }

使用Parallel.ForEach()代码看起来像你的代码,因为ForEach()末尾有一个隐含的WaitAll()。

答案 3 :(得分:1)

正如Matt的解释详细说明,在子线程上抛出的异常不会冒泡到父线程。但是,如果您希望能够从子线程中捕获异常,这是一种方法:

class Program {
    static void Main(string[] args) {
        Action action = BeginCheckExc;
        IAsyncResult result = action.BeginInvoke(new AsyncCallback(EndCheckExc), null);

        try {
            action.EndInvoke(result);
        }
        catch (Exception ex) { // Exception is caught here
            Console.WriteLine(ex.Message);
        }
    }

    static void BeginCheckExc() {
        Thread.Sleep(3000); // Simulate long operation
        throw new Exception("Oops! Something broke!");
    }

    static void EndCheckExc(IAsyncResult result) {
        Console.WriteLine("Done");
        Console.ReadLine();
    }
}

您将看到的输出类似于:

  

完成。

     

糟糕!东西坏了!

     

按任意键继续......

答案 4 :(得分:0)

正如其他人所说,“因为它处于不同的主题”。换句话说,为什么第一个线程会因为另一个线程遇到异常而受到打扰?

如果您使用BackgroundWorker,则异常会在RunWorkerCompleted事件的参数中传递(当线程完成时) - 这样可能会更容易,具体取决于您的情况。

答案 5 :(得分:0)

如果您对从所有线程捕获未处理的排除感兴趣,可以添加应用程序范围的未处理异常处理程序。查看Application.ThreadException&的文档。 AppDomain.UnhandledException

基本上,您需要处理的代码是:

Application.ThreadException += new ThreadExceptionEventHandler(ThreadExceptionFunction);
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(UnhandledExceptionFunction);

public static void ThreadExceptionFunction(object sender, ThreadExceptionEventArgs args)
{
     // Handle the exception.          
}

public static void UnhandledExceptionFunction(object sender, UnhandledExceptionEventArgs args)
{
     // Handle the exception. 
}

请记住,在异常之后,您的应用程序可能处于损坏状态,因此最好在记录错误后尽快退出。