多线程异常传递

4
我有一个应用程序处理事件回调,在我的情况下是SerialPort的DataReceived事件。在该回调中,我有业务逻辑需要在另一个线程上引发异常。该线程正在等待事件监听器发送消息或让它知道已发生异常。
最佳方法是如何在线程之间保留堆栈跟踪?
简单地将线程传递给工作线程并重新抛出它会导致堆栈跟踪丢失。

它真的需要是一个异常吗?你可以通过其他方式报告状态,这可能更容易且更有效率。 - Jon B
这是一个非常模糊的问题。有很多很多种方法可以做到这一点。 - Botonomous
我会在工作线程中捕获异常,并将异常对象传递到主线程,在那里您可以根据需要处理它。 - Polyfun
1
@JonB,可能会发生各种异常,其中一些是我们创建的内部异常,许多异常在 .net 中很常见,例如空对象引用。无论如何,我们希望该异常在另一个线程中上升。 - Lucas B
7个回答

2

如果你在使用.NET 4.5及以上版本,则可以使用ExceptionDispatchInfo,代码如下:

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

如果您使用的是.NET 4.0,则必须使用更加hackish的解决方案:
Exception exception = ...;
typeof(Exception).InvokeMember("PrepForRemoting",
    BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.InvokeMethod,
    null, exception, new object[0]);
throw exception;

2
  • 根据您的方法,例如TPL:throw-->AggregateException。
  • BackGroundWorker--> 您必须处理结果中的错误。
  • 线程--> 您必须将错误传递到主线程。
  • 任务--> throw--> AggregateException。
  • 异步/等待--> 也会抛出AggregateException(我不确定)。

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

异步/等待非常灵活。

BackGroundWorker是过时但有时仍然需要。

使用回调进行异步编程(在您的情况下也已经过时),但仍然可以使用;我建议您使用任务。

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


0

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

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

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


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方法之间没有确切的时间边界,因此我们可以替换异常列表而不会受到任何惩罚(并且可以避免使用线程同步)。


0

这里有两个建议:

首先,我建议在事件处理程序(串口线程上)中处理异常,并通知主线程执行您想要的操作。

如果您真的想在主线程上处理异常,您也可以在主线程上调用您想要执行的工作(甚至整个事件处理程序,如果您喜欢)。这样,异常将首先在主线程上抛出。如果需要进行大量的工作,您可以将其卸载到任务中(并像这样处理异常)。这还具有不阻塞串口线程和通过运行缓冲区空间而潜在丢失数据的优点。


0

我相信你的问题只是需要解决方案,
正如你所说的:这个线程正在等待事件监听器发送消息,或者让它知道发生了异常。

既然你已经有一个等待的事件监听器,同样的方式你可以发送 异常 而不是消息,使得线程采取适当的措施。


0

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接