多线程异常冒泡

时间:2014-04-25 12:43:31

标签: c# exception-handling event-handling

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

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

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

7 个答案:

答案 0 :(得分:2)

  • 这取决于您的方法,例如TPL:throw - > AggregateException。
  • 的BackgroundWorker - >你必须要注意结果中的错误。
  • 帖子 - >你必须把错误编组到主线程上。
  • 任务 - >扔 - > AggregateException。
  • 异步/ AWAIT - >抛出也是AggregateException(我不确定)。

任务方法提供了一个继续来处理由先行者抛出的异常和良好的错误处理。

异步/等待非常灵活。

BackGroundWroker是遗产,但有时仍然需要。

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

AggregateException :表示应用程序执行期间发生的一个或多个错误。您将从根AggregateException

中获取异常列表(来自其他线程)

答案 1 :(得分:2)

如果您使用的是.NET 4.5,那么可以使用ExceptionDispatchInfo

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;

答案 2 :(得分:0)

以下是两条建议:

首先,我会考虑在事件处理程序中处理异常(在串行端口的线程上),并发信号通知主线程做任何你想做的事情。

如果你真的想在主线程上处理异常,你也可以调用你想在主线程上做的工作(如果你愿意,甚至可以调用整个事件处理程序)。这样,异常将首先抛出主线程。如果要做繁重的工作,可以将其卸载到任务(and handle exceptions like this)。这样做的好处是不会阻塞串口的线程,并且可能因为缓冲区空间不足而丢失数据。

答案 3 :(得分:0)

我相信你的问题只能解决你的问题,
正如你所说That thread is waiting for the event listener to send it a message, or let it know an exception has occurred.

由于您已经有一个线程等待的事件监听器,因此您可以发送Exception而不是消息并触发线程以采取适当的操作。

答案 4 :(得分:0)

一种方式,主要是"保留堆栈跟踪是将异常传递给另一个线程,然后使用相同的类型重新创建该异常,但是将原始异常作为InnerException传递:

// retain the exception type, but don't lose the stack trace.
Exception instance = (Exception)Activator.CreateInstance(ex.GetType(), "", ex);
throw instance;

它并不完美,但至少堆栈跟踪不会丢失。

答案 5 :(得分:0)

我从来没有尝试过这个,但是.Net4.5 ExceptionDispatchInfo中有一个新类可以用来保存异常的堆栈跟踪。看看this

答案 6 :(得分:0)

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

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

由于线程和Enqueue方法之间没有确切的时间边界,我们可以像没有任何惩罚那样替换异常列表(并且可以避免使用线程同步)。