C# 多线程异常冒泡
我有一个处理事件回调的应用程序,在我的例子中,它是SerialPort上的DataReceived事件。在该回调中,我有业务逻辑,需要在另一个线程上引发异常。该线程正在等待事件侦听器向其发送消息,或让其知道发生了异常 跨线程保留堆栈跟踪的最佳方法是什么?C# 多线程异常冒泡,c#,exception-handling,event-handling,C#,Exception Handling,Event Handling,我有一个处理事件回调的应用程序,在我的例子中,它是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(我不确定)
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中很常见,比如空对象引用。不管怎样,我们都希望该异常出现在另一个线程上。