在C#中缓存异常实例是一种好习惯

时间:2016-02-22 08:14:53

标签: c# multithreading exception caching exception-handling

我试图在这个问题上找到答案,即:

以下代码是一个好习惯吗? 我应该尝试在任何可能或不可能的地方重现它吗?如果没有,为什么?

public class ExceptionHelper
{
    private static readonly HttpException _errorReadingRequest = new HttpException(500, "Error reading request. Buffer is empty.");
    public static Exception ErrorReadingRequest { get { return _errorReadingRequest; } }
}

public class ExceptionThrower
{
    public static void ThrowCached1()
    {
        throw ExceptionHelper.ErrorReadingRequest;
    }
    // ...
}

我试图在缓存的实例中放入几个地方,它似乎保留了堆栈跟踪,from MSDN

  

堆栈跟踪从抛出异常的语句开始   并以捕获异常的catch语句结束。意识到   在决定将抛出语句放在何处时,这一事实。

我理解为" 它将堆栈跟踪存储在throw,而不是创建"。但是,我想知道当多个线程尝试抛出相同的缓存异常实例时是否会出现问题。

在同一个MDSN页面上,他们正在使用一个辅助函数,看起来在框架中,他们正在做同样的事情:帮助函数重新创建异常并抛出。

注意:我知道这不是常见的情况,因为大多数情况下,您希望将特定邮件存储在例外中,以便更容易理解发生了什么。< / p>

也许问题只是抛出关键字线程安全

上下文

我偶然发现了这种代码,同时代码审查了一些性能敏感的应用程序代码。目标是在启动期间创建最大实例,然后在执行期间从不(或几乎从不)实例,以限制GC性能影响(特别是因为GCHandle事件发生堆碎片)。我对这段代码感觉不好,但需要事实来支持我的重构。

3 个答案:

答案 0 :(得分:9)

通常,有一个缓存的异常是没有意义的。创建一个对象很快。例外情况很少见 - 捕捉它们有一些开销,就像投掷它们一样。除了以下情况之外,没有任何事情可以解决,除了:

  • 线程问题
  • 更复杂的代码
  • 完全忽略了我在过去20年中所看到的编码标准。

如果这些事情中的任何一个是你的目标 - 继续。

  

也许问题只是抛出关键字线程是否安全?

throw关键字是线程安全的 - 您可以同时抛出多个线程。使用相同的异常会让你遇到麻烦,但这不是因为抛出KEYWORD不是线程安全的,而是因为你通过传入相同的数据对象而恶意侵犯了线程安全。

答案 1 :(得分:1)

抛出异常会改变该异常对象。它存储堆栈例如。如果要将堆栈跟踪用于记录目的,则会出现此问题。你会得到随机损坏的堆栈。

此外,创建异常对象很便宜。这没有太大成就。

答案 2 :(得分:1)

@TomTom给出了一个很好的答案,我只想指出异常缓存作为标准做法的示例:TPL's Task class Exception属性。

有时,如果在某些异步操作中抛出异常,最好在需要操作结果时保存有关它的信息。

但是!正如TPL的程序员所做的那样,你应该原始异常包装在新的异常中(例如,AggregateException),这样你就可以看到它的原始来源和堆栈跟踪。

至于GC集合的性能影响,您可以使用其他技术。例如,如果正在创建大量对象,并且在收集之后,请尝试使用DTO对象的结构而不是类。如果它们适合堆栈大小,它们将在方法结束后自动收集。