c# - 使用集合时如何实现线程安全的缓存机制?

标签 c# asp.net-mvc caching .net-4.0 thread-safety

场景:

  • 我有一堆 Child 对象,都与给定的 Parent 相关。
  • 我正在开发 ASP.NET MVC 3 Web 应用程序(例如多线程)
  • 其中一个页面是“搜索”页面,我需要在其中获取给定的子集子集并在内存中对他们“做一些事情”(计算、排序、枚举)
  • 我没有让每个 child 单独调用,而是对数据库进行一次调用以获取给定 parent 的所有 child ,缓存结果并对结果“执行操作”。
  • 问题是“做事”涉及 LINQ 操作(枚举、从集合中添加/删除项目),当使用 List<T> 实现时不是线程安全的。

我已经阅读了关于 ConcurrentBag<T> 的内容和 ConcurrentDictionary<T>但不确定我是否应该使用其中之一,或者自己实现同步/锁定。

我在 .NET 4 上,所以我正在使用 ObjectCache作为 MemoryCache.Default 的单例实例.我有一个与缓存一起工作的服务层,服务接受 ObjectCache 的实例,这是通过构造函数 DI 完成的。这样所有服务共享相同的 ObjectCache实例。

主要的线程安全问题是我需要遍历当前的“缓存”集合,如果我正在处理的 child 已经存在,我需要删除它并添加我正在处理的那个,此枚举是导致问题的原因。

有什么建议吗?

最佳答案

是的,要有效地实现缓存,它需要一个快速查找机制等List<T>是开箱即用的错误数据结构。 Dictionary<TKey, TValue>是缓存的理想数据结构,因为它提供了一种替换方式:

var value = instance.GetValueExpensive(key);

与:

var value = instance.GetValueCached(key);

通过使用字典中的缓存值并使用字典来完成查找的繁重工作。来电者一无所知。

但是,如果调用者可以从多个线程调用,那么 .NET4 会提供 ConcurrentDictionary<TKey, TValue>在这种情况下效果很好。但是字典缓存了什么?在您的情况下,字典键似乎是 child ,而字典值是该 child 的数据库结果

好的,现在我们有一个线程安全且高效的数据库结果缓存,由子键控。我们应该为数据库结果使用什么数据结构?

您没有说这些结果是什么样的,但是由于您使用了 LINQ,我们知道它们至少是 IEnumerable<T>甚至可能List<T> .所以我们又回到了同样的问题,对吧?因为List<T>不是线程安全的,我们不能将它用于字典值。或者我们可以吗?

从调用者的角度来看,缓存必须是只读的。您说使用 LINQ 可以“执行一些操作”,例如添加/删除,但这对于缓存值毫无意义。只有在缓存本身的实现中做一些事情才有意义,例如用新结果替换陈旧的条目。

字典值,因为它是只读的,可以List<T> 没有不良影响,即使它会被多个线程访问。您可以使用 List<T>.AsReadOnly以提高您的信心并添加一些编译时安全检查。

但重要的一点是 List<T>如果它是可变的,则它不是线程安全的。由于根据定义,如果多次调用使用缓存实现的方法必须返回相同的值(直到缓存本身使该值无效),客户端无法修改返回值,因此 List<T>必须卡住,实际上是不可变的。

如果客户端迫切需要修改缓存的数据库结果并且值为List<T> , 那么唯一安全的方法是:

  1. 复印一份
  2. 修改文案
  3. 请求缓存更新值

总而言之,对顶级缓存使用线程安全的字典,对缓存值使用普通列表,注意不要在将最后一个插入缓存后修改其内容。

关于c# - 使用集合时如何实现线程安全的缓存机制?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6132238/

相关文章:

javascript - 复选框 ajax 请求选中/取消选中

java - CouchBase 客户端与 Spymemcache,客户端使用

Django 防止缓存包含的 html 片段

c# - C# 中的简单归并排序

c# - 无法加载文件或程序集 'System.Web.Mvc, Version=3.0.0.0' 或其依赖项之一

jquery - 使用 asp.net mvc 中的单选按钮进行远程验证

c# - 一个 ASP.NET 页面在 0 毫秒内下载是不是太快了?

c# - 如何从反射执行显式操作转换?

c# - WP7如何让网格尺寸适配图像尺寸

c# - WebSocket 客户端的状态不会因网络丢失而改变