【问题标题】:Reduce initialization frequency in ASP.NET via cache, ThreadStatic, etc通过缓存、ThreadStatic 等降低 ASP.NET 中的初始化频率
【发布时间】:2012-08-08 13:57:14
【问题描述】:

我想跨多个并发 ASP.NET 请求使用非线程安全编码器实例池,而无需重新初始化它们。如果我不关心初始化成本,我会这样做:

public class ResultController {
    public JsonResult GetResults() {
        List<MyViewModel> items = new List<MyViewModel>();
        // It obviously does more than this in real life
        for(id = 0; id < 1000; id++) {
            items.Add(new MyViewModel(id));
        }
        return Json(items);
    }
}

public class MyViewModel() {
    public string CodedId { get; set; }
    public MyViewModel(int id) {
        // This "new" is the concern
        CodedId = new ExpensiveToInitializeCodec().InexpensiveEncode(id);
    }
}

工作正常。所有本地人,不用担心线程,在我的模型之外没有人需要了解该值是编码的。然而,一个快速的性能测试表明,每次初始化大约需要 0.129 毫秒,而编码本身需要不到 0.006 毫秒。 (仅供参考,编解码器是 TripleDESCryptoServiceProvider)。

我想在不传递预初始化对象的情况下限制初始化成本(例如传入构造函数以提高性能,但会破坏关注点分离)。这是我目前正在做的事情,在这个简单的例子之外它显然会变得混乱:

public class ResultController {
    public JsonResult GetResults() {
        List<MyViewModel> items = new List<MyViewModel>();
        ExpensiveToInitializeCodec codec = new ExpensiveToInitializeCodec();
        for(id = 0; id < 1000; id++) {
            items.Add(new MyViewModel(id, codec));
        }
        return Json(items);
    }
}

public class MyViewModel() {
    public string CodedId { get; set; }
    public MyViewModel(int id, ExpensiveToInitializeCodec codec) {
        CodedId = codec.InexpensiveEncode(id);
    }
}

我可以利用众所周知的 ASP.NET 每请求缓存模式:

public class MyViewModel() {
    public string CodedId { get; set; }
    public MyViewModel(int id) {
        CodedId = ExpensiveToInitializeCodec.Get().InexpensiveEncode(id);
    }
}

public class ExpensiveToInitializeCodec {
    public static ExpensiveToInitializeCodec Get() {
        ExpensiveToInitializeCodec codec = HttpContext.Current.Items["codec"];
        if (codec == null) {
            codec = new ExpensiveToInitializeCodec();
            HttpContext.Current.Items["codec"] = codec;
        }
        return codec;
    }
}

但是在一个紧密的循环中运行仍然很浪费:How much computation is behind a HttpContext.Current call?

似乎每个线程的解决方案可能比每个请求的解决方案更精确。任何与 ASP.NET 请求兼容的建议?

在 ASP 之外但仍在 .NET 空间中,一个人的答案是 ThreadStatic: Using ThreadStatic to replace expensive locals -- good idea? 。但是,http://blog.idm.fr/2010/03/aspnet-thread-agility-or-why-threadstatic-should-not-be-used.html 显然排除了 ASP.NET 中的该解决方案。与我的Is there any way to imitate ThreadStatic for use with HttpContext.Current.Items? 类似的问题无人回答。

编辑:如果我确保我的编解码器使用不与 I/O 操作交错,我似乎可以使用 ThreadStatic,但我不确定这有多安全。

我现在达到了,但我想到的其他一些方法包括 1) 为具有 EncodedAttribute 的项目提供自定义序列化/反序列化,2) 实现我自己的静态 TripleDES 单块加密器,没有实例初始化开销, 3)根据我的喜好,将外部初始化的加密器传递给每个项目的构造函数,4)实现 IEncryptable 接口并在填充结果后重新枚举项目,5)执行视图模型外部的所有加密,在任何地方都实现使用了视图模型。

【问题讨论】:

  • 你是什么意思:“没有传递预先初始化的对象(并打破分离)。”这是否意味着您不想要这些对象的“资源池”?如果不是,我不理解反对意见。
  • @ZeroBugBounce :不,这些对象的资源池会很好用。我不想要的是在父类中创建一个共享实用程序对象,只是将其传递给将附加到该父类的所有子类的初始化过程,从而牺牲对性能的关注点分离。这就是我目前正在做的事情。我有多个需要此功能的子类和多个父类,这是一个纠结。我添加了一个代码示例。
  • 您是说父子之间的实际实例需要协调吗?如果没有,父母和孩子是否可以简单地从共享池中检查他们自己的实例并在完成后返回它们?或者,如果是这样,您是否有某种 IoC 容器可以根据每个请求完成协调结帐的工作?
  • @ZBB:不需要协调。工作对象池可能正是正确的解决方案。我从来没有建过一个。同步并没有吓到我,但在我看来,意外使其饱和将是很大的风险,需要小心谨慎并编写代码来防止。 stackoverflow.com/questions/2510975/… 。感谢您的想法,我正在阅读它。

标签: asp.net performance caching thread-safety


【解决方案1】:

访问 HttpContext.Current.Items 的性能成本真的是一个问题,还是您只是在猜测?根据您给出的数字和您给出的问题中的数字以及链接问题中的数字,它不应该是: - 您有 129 毫秒用于单次初始化 + 6 毫秒用于 1000 次迭代 - HttpContext 在 10K 次迭代中需要 0.1 毫秒(即在您的情况下为 0.01)

线程静态是个坏主意。虽然它可能会起作用,但您将来将无法使用异步控制器和类似功能。

【讨论】:

  • 谢谢。是的,我只是根据几篇文章进行推测,尽管我对此进行了测试。我会更新的。
  • 你是对的。保存实例的 10000 次编解码器迭代运行 60 毫秒,添加缓存查找增加了 1 毫秒。相对于 1.5 秒的初始化而言无关紧要。感谢您的回复。
  • 实现了当前的上下文缓存,没有任何明显的负载测试变化。不知道我是如何误读我链接的那篇文章的。我认为这意味着成本更高。
猜你喜欢
  • 2011-05-15
  • 2020-12-03
  • 2010-09-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-11-13
  • 1970-01-01
  • 2018-02-10
相关资源
最近更新 更多