我有一个应用程序处理事件回调,在我的情况下是SerialPort的DataReceived事件。在该回调中,我有业务逻辑需要在另一个线程上引发异常。该线程正在等待事件监听器发送消息或让它知道已发生异常。
最佳方法是如何在线程之间保留堆栈跟踪?
简单地将线程传递给工作线程并重新抛出它会导致堆栈跟踪丢失。
最佳方法是如何在线程之间保留堆栈跟踪?
简单地将线程传递给工作线程并重新抛出它会导致堆栈跟踪丢失。
如果你在使用.NET 4.5及以上版本,则可以使用ExceptionDispatchInfo
,代码如下:
Exception exception = ...;
ExceptionDispatchInfo.Capture(exception).Throw();
Exception exception = ...;
typeof(Exception).InvokeMember("PrepForRemoting",
BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.InvokeMethod,
null, exception, new object[0]);
throw exception;
任务方法提供了一个继续来处理由前置项抛出的异常和良好的错误处理。
异步/等待非常灵活。
BackGroundWorker是过时但有时仍然需要。
使用回调进行异步编程(在您的情况下也已经过时),但仍然可以使用;我建议您使用任务。
AggregateException:表示应用程序执行期间发生的一个或多个错误。您将在根AggregateException中获得来自其他线程的异常列表。
保留堆栈跟踪的“主要”方法是将异常传递到另一个线程,然后使用相同类型重新创建该异常,但将原始异常作为InnerException传递:
// retain the exception type, but don't lose the stack trace.
Exception instance = (Exception)Activator.CreateInstance(ex.GetType(), "", ex);
throw instance;
它并不完美,但至少堆栈跟踪不会丢失。
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方法之间没有确切的时间边界,因此我们可以替换异常列表而不会受到任何惩罚(并且可以避免使用线程同步)。
这里有两个建议:
首先,我建议在事件处理程序(串口线程上)中处理异常,并通知主线程执行您想要的操作。
如果您真的想在主线程上处理异常,您也可以在主线程上调用您想要执行的工作(甚至整个事件处理程序,如果您喜欢)。这样,异常将首先在主线程上抛出。如果需要进行大量的工作,您可以将其卸载到任务中(并像这样处理异常)。这还具有不阻塞串口线程和通过运行缓冲区空间而潜在丢失数据的优点。
我相信你的问题只是需要解决方案,
正如你所说的:这个线程正在等待事件监听器发送消息,或者让它知道发生了异常。
既然你已经有一个等待的事件监听器,同样的方式你可以发送 异常
而不是消息,使得线程采取适当的措施。