一个 C# 异常是否可以在不同的线程上多次引发

本文关键字:线程 异常 是否 一个 | 更新日期: 2023-09-27 18:35:33

我有一个实现供应商/消费者模式的C#服务器。此服务器具有生成一些数据的供应商线程。多个使用者线程使用数据。对数据的访问已正确锁定,因此一切正常。但有时供应商线程无法获取数据。在这种情况下,它会引发异常。在同一线程中捕获异常。但是在发生异常后,有必要在每个使用者线程上抛出相同的异常。所以我有一个问题:在多个线程上多次抛出一个异常对象是否安全?我知道 CLR 异常和所有继承异常的类都必须具有[Serializable]属性。这可能表明 Exception 对象在抛出时被序列化,但我找不到任何相关信息。

一个 C# 异常是否可以在不同的线程上多次引发

在多个线程上多次抛出一个异常对象是否安全?

的,它是安全的,假设您的异常对象是线程安全的。只要确保在多个线程上重新抛出Exception对象本身是线程安全的,那么在多个位置重用同一对象就不会有问题*

但是,每个使用者线程最好抛出自己的异常来响应原始异常,并将原始异常用作构造函数中的innerException参数。内部异常将在所有线程之间保持共享,但嵌套结构将更好地反映从捕获异常的用户的角度来看发生的情况。

如果重新引发原始异常,则捕获它的用户将只能找到原始抛出现器,即生产者线程。这可能会令人困惑,因为它们从使用者线程接收异常,因此它们很难将导致异常的事件链拼凑在一起,因为堆栈跟踪将关闭。

另一方面,嵌套异常来自使用者线程,但它们也提供innerException作为其根本原因,因此捕获器可以更好地了解到底发生了什么。

我知道 CLR 异常和所有继承异常的类都必须具有[Serializable]属性。这可能表明 Exception 对象在抛出时被序列化,但我找不到任何相关信息。

添加[Serializable]属性的原因在此问答中描述。它对同一应用域中多个线程上的异常的可用性没有影响。

*假设重新抛出发生在不同线程上相同方法的同一位置。

异常可以序列化,但不必序列化。例如,如果它必须使用远程处理跨 AppDomain,它将被序列化,但在您的方案中,情况似乎并非如此。

除此之外,通常的线程和同步问题也适用:如果异常仅在构造函数中设置只读状态(通常是这种情况),则异常本质上是线程安全的。

不过,还有一件事:每当重新抛出单个异常实例时,您都会覆盖它。最后一个投掷它的位置获胜。因此,将您的异常包装在另一个中可能是一个更好的主意(例如 TargetInvocationException)。