为什么在threadPool线程上引发的异常不在c#的主线程中处理

本文关键字:线程 处理 threadPool 为什么 异常 | 更新日期: 2023-09-27 17:50:37

我有以下代码片段:

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();
    }
}

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

为什么在threadPool线程上引发的异常不在c#的主线程中处理

假设你有一条主路(a),另一条路从这条主路(B)岔开。

当一辆卡车沿着a行驶时,如果它撞车了,那么a就会知道,交通就会停止。

当一辆卡车沿着B行驶时,如果它撞车了,那么B就会知道,交通就会停止。

但是B通过什么机制告诉A一辆卡车撞到它了呢?

一旦卡车在B上,它就不会影响A,除非沿着A有另一个进入B的入口。

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

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

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

任务并行库(TPL) 处理(catch &为您传播异常。

但是异常出现在您等待任务完成的点,而不是在您启动任务的点/线程上。

    // 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()。

正如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();
    }
}
您将看到的输出类似于:

哦!东西坏了!

按任意键继续…

正如其他人所说,"因为它在不同的线程中"。或者换句话说,为什么第一个线程会因为另一个线程遇到异常而受到困扰?

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

如果想从所有线程中捕获未处理的异常,可以添加应用程序范围内的未处理异常处理程序。请查看Application的文档。ThreadException,应用程序域中。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. 
}

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