【问题标题】:Is it a good practice to cache exception instance in C#在 C# 中缓存异常实例是一种好习惯吗
【发布时间】:2016-02-22 08:14:52
【问题描述】:

我正在尝试找到关于该问题的答案,即:

下面的代码是一个好习惯吗?
我是否应该尽可能地复制它?如果不是,为什么?

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 页面上,他们使用了一个辅助函数,而且在框架中,他们似乎也在做同样的事情:重新创建异常并抛出异常的辅助函数。

注意:我知道这不是一种常见情况,因为大多数时候,您希望将特定消息存储在异常中,以便更容易理解正在发生的事情。

也许问题只是 throw 关键字 thread safe 还是 not

上下文:

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

【问题讨论】:

  • 为什么要缓存它?你看到了什么好处?我很好奇,因为我没有看到任何真正的好处。
  • 在某些特定应用程序中,当您不想在执行期间创建任何实例时(或限制 GC 性能影响的最小值)。顺便说一句,我发现了这种代码,想知道它是否有效。好像没有。
  • 如您所见,早期优化仍然是根本...

标签: c# multithreading exception caching exception-handling


【解决方案1】:

一般来说,缓存一个异常是没有意义的。创建对象很快。例外应该是罕见的 - 捕捉它们有一些开销,就像扔它们一样。拥有“一个例外来统治一切”根本没有任何收获,除了:

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

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

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

throw 关键字是线程安全的——你可以有多个线程同时抛出。虽然使用相同的异常会给你带来麻烦,但这并不是因为 throw KEYWORD 不是线程安全的,而是因为你通过传入相同的数据对象恶意违反了线程安全。

【讨论】:

    【解决方案2】:

    抛出异常会改变该异常对象。例如,它存储堆栈。如果您想将堆栈跟踪用于记录目的,这是一个问题。你会得到随机损坏的堆栈。

    另外,创建一个异常对象很便宜。这并没有太大的作用。

    【讨论】:

      【解决方案3】:

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

      有时,如果在某个异步操作中抛出异常,最好将有关它的信息保存起来以备不时之需。

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

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

      【讨论】:

      • 但那些不共享。异常属性用于保存一个特定异常,目的是在评估结果时重新抛出该异常。存储异常是有意义的 - 我什至在某些业务对象状态下长期这样做(对象失败 - 新状态:错误,异常属性具有捕获的异常)。但我不会在各种场景中重复使用它。
      • @TomTom 是的,错过了主要思想。但是关于 GC 的建议仍然有用(在某些情况下)。
      • 是的,除非你有一个 GC 问题,这要归功于异常 - 你的代码中确实有问题;)
      • @TomTom 我认为这与一种晦涩的意识形态有关,即启动后禁止使用“新”关键字。我会看看我能做些什么。
      猜你喜欢
      • 2018-02-27
      • 1970-01-01
      • 2012-08-22
      • 2018-01-17
      • 1970-01-01
      • 1970-01-01
      • 2017-02-20
      • 1970-01-01
      • 2015-09-02
      相关资源
      最近更新 更多