Frage

I have an application that handles an event callback, in my case it is the DataReceived event on a SerialPort. In that callback I have business logic that needs to have an exception raised on another thread. That thread is waiting for the event listener to send it a message, or let it know an exception has occurred.

What is the best way to retain the stack trace across threads?

A simple passing the thread over to the worker thread and rethrowing it, causes the stack trace to be lost.

War es hilfreich?

Lösung

  • It is depending on your approach for example TPL: throw--> AggregateException.
  • BackGroundWorker--> you have to take care about the error in result.
  • Threads--> you have to marshall the error to the main thread.
  • Tasks--> throw--> AggregateException.
  • Async/await--> throw also AggregateException (I'm not sure).

Tasks approach offer a continuation to handle exceptions thrown by the antecedent and good error handling.

Async/await very flexible.

BackGroundWroker is legacy but still sometimes required.

Asynchronous programming with callbacks (in your case is also legacy) but it can be used; I recommend you to use the Tasks.

AggregateException: Represents one or more errors that occur during application execution. You will get a list of exceptions(from other thread) in the root AggregateException

Andere Tipps

If you are on .NET 4.5, then you can use ExceptionDispatchInfo as such:

Exception exception = ...;
ExceptionDispatchInfo.Capture(exception).Throw();

If you are on .NET 4.0, then you have to use a much more hackish solution:

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

Here are two suggestions:

First, I would consider handling the exception in the event handler (on the serial port's thread), and signalling the main thread to do whatever it is you want to do.

If you really want to handle the exception on the main thread, you could also invoke the work you want to do on the main thread (even the entire event handler if you like). This way the exceptions will be thrown on the main thread in the first place. If there is heavy lifting to do, you could offload that to a task (and handle exceptions like this). This also has the advantage of not blocking the serial port's thread and potentially losing data by running out of buffer space.

I believe your question is only having the answer to your problem,
As you stated That thread is waiting for the event listener to send it a message, or let it know an exception has occurred.

As you are already having an event listener to which thread waits, the same way you can send the Exception instead of message and get the thread triggered to take proper action.

One way to "mostly" retain the stack trace is to pass the exception over to the other thread and then recreate that exception using the same type, but pass the original exception in as an InnerException:

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

It isn't perfect, but at least the stack trace doesn't get lost.

I've never tried this, but there is a new class in .Net4.5 ExceptionDispatchInfo which can be used to preserve the stack trace of a exception. Take a look at this

If the thread is part of the same class, just add it do a list and throw it when the other method is invoked:

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

As there are no exact temporal boundary between the thread and the Enqueue method we can replace the exception list like that without any penalties (and can avoid using thread synchronization).

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top