C# 多线程异常冒泡

C# 多线程异常冒泡,c#,exception-handling,event-handling,C#,Exception Handling,Event Handling,我有一个处理事件回调的应用程序,在我的例子中,它是SerialPort上的DataReceived事件。在该回调中,我有业务逻辑,需要在另一个线程上引发异常。该线程正在等待事件侦听器向其发送消息,或让其知道发生了异常 跨线程保留堆栈跟踪的最佳方法是什么? 简单地将线程传递给工作线程并重新执行,会导致堆栈跟踪丢失。以下是两个建议: 首先,我考虑在事件处理程序中处理异常(在串行端口的线程上),并对主线程进行信令,以完成您想做的任何事情。 如果确实希望在主线程上处理异常,还可以调用希望在主线程上执行的

我有一个处理事件回调的应用程序,在我的例子中,它是SerialPort上的DataReceived事件。在该回调中,我有业务逻辑,需要在另一个线程上引发异常。该线程正在等待事件侦听器向其发送消息,或让其知道发生了异常

跨线程保留堆栈跟踪的最佳方法是什么?


简单地将线程传递给工作线程并重新执行,会导致堆栈跟踪丢失。

以下是两个建议:

首先,我考虑在事件处理程序中处理异常(在串行端口的线程上),并对主线程进行信令,以完成您想做的任何事情。


如果确实希望在主线程上处理异常,还可以调用希望在主线程上执行的工作(如果愿意,甚至可以调用整个事件处理程序)。这样,异常将首先在主线程上抛出。如果有繁重的工作要做,您可以将其卸载到任务()中。这还有一个优点,即不会阻塞串行端口的线程,也不会因为缓冲区空间不足而可能丢失数据。

我相信您的问题只会回答您的问题,
正如您所述,
线程正在等待事件侦听器向其发送消息,或者让它知道发生了异常。

由于您已经有了一个线程等待的事件侦听器,您可以用同样的方法发送异常而不是消息,并触发线程以采取适当的操作。

一种“大部分”保留堆栈跟踪的方法是将异常传递给另一个线程,然后使用相同的类型重新创建该异常,但将原始异常作为InnerException传入:

// retain the exception type, but don't lose the stack trace.
Exception instance = (Exception)Activator.CreateInstance(ex.GetType(), "", ex);
throw instance;
它并不完美,但至少堆栈跟踪不会丢失。

  • 这取决于您的方法,例如TPL:throw--> 聚合异常
  • BackGroundWorker-->您必须注意结果中的错误
  • 线程-->必须将错误整理到主线程
  • 任务-->抛出-->聚合异常
  • Async/await-->还抛出AggregateException(我不确定)
Tasks方法提供了一个继续来处理由先行项引发的异常以及良好的错误处理

异步/等待非常灵活

BackgroundRoker是传统的,但有时仍然需要

带有回调的异步编程(在您的情况下也是遗留的),但可以使用它;我建议您使用这些任务


AggregateException:表示在应用程序执行期间发生的一个或多个错误。您将在根AggregateException中获得一个异常列表(来自其他线程)

我从未尝试过此操作,但是.Net4.5中有一个新类可用于保留异常的堆栈跟踪。查看一下

如果您使用的是.NET 4.5,则可以使用
例外的DispatchInfo

Exception exception = ...;
ExceptionDispatchInfo.Capture(exception).Throw();
如果您使用的是.NET 4.0,那么您必须使用一种更黑客的解决方案:

Exception exception = ...;
typeof(Exception).InvokeMember("PrepForRemoting",
    BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.InvokeMethod,
    null, exception, new object[0]);
throw exception;

如果线程是同一类的一部分,只需将其添加到一个列表中,并在调用另一个方法时抛出它:

public class MyProcessor
{
    List<Exception> _threadExceptions = new List<Exception>();

    public void Enqueue(SomeJob job)
    {
        if (_threadExceptions.Count > 0)
        {
            var myList = _threadExceptions; 
            _threadExceptions = new List<Exception>();
            throw new AggregateException(myList);
        }

        //do some work
    }

    private static void YourThreadFunc()
    {
        try
        {
            //listen to the port
        }
        catch (Exception ex)
        {
            _threadExceptions.Add(ex);
        }
    }
}
公共类MyProcessor
{
列表_threadExceptions=新列表();
公共void排队(SomeJob作业)
{
如果(_threadExceptions.Count>0)
{
var myList=\u线程异常;
_threadExceptions=新列表();
抛出新的AggregateException(myList);
}
//做些工作
}
私有静态void YourThreadFunc()
{
尝试
{
//听港口
}
捕获(例外情况除外)
{
_添加(ex);
}
}
}

由于线程和排队方法之间没有精确的时间边界,我们可以这样替换异常列表,而不会受到任何惩罚(并且可以避免使用线程同步)。

它真的需要是异常吗?你可以用其他更容易、更有效的方式报告状态。这是一个非常模糊的问题。有很多方法可以做到这一点。我会在工作线程中捕获异常,并将异常对象传递给主线程,您可以在主线程中根据需要处理它。@JonB可能会发生各种异常,其中一些是我们创建的内部异常,许多异常在.net中很常见,比如空对象引用。不管怎样,我们都希望该异常出现在另一个线程上。