【问题标题】:what will happen if not lock the dictionary when i modify it? about asp.net cache如果我修改字典时不锁定字典会怎样?关于asp.net缓存
【发布时间】:2009-12-18 18:04:38
【问题描述】:

对不起,我有很多关于锁/缓存的问题。=_=..

->1。关于缓存,我知道asp.net中的缓存是线程安全的,我常用的简单代码是

    IList<User> user= HttpRuntime.Cache["myCacheItem"] as IList<User>;
    if (user == null)
    { 
       //should i have a lock here?
       //lock(some_static_var){...}
       HttpRuntime.Cache["myCacheItem"] = GetDateFromDateBase();
    }

    return user;

应该在代码中使用锁吗?

->->1.1 如果我使用,也许我应该声明许多锁定项?我在社区服务器中看到了一些实现,它使用静态字典来存储 lockitem,这是个好主意吗?因为我担心字典中的锁定项可能太多,可能会减慢系统速度。

->->1.2 如果我不使用,会发生什么?可能只有两个或更多线程访问GetDateFromDateBase()?如果只是这样,我想也许我可以放弃锁定。

->2.我有一个通用字典存储在缓存中,我必须修改(添加/更新/删除)它。我只是用它来获取像 dic.trygetvalue(key) 这样的值,不要循环它。

->->2.1 如果我能保证修改只发生在一个线程中,那么场景就像
a.aspx -> 从缓存中读取字典,并显示在页面上,对用户公开
b.ashx -> 调用时会修改字典。(循环5分钟),私有使用

我应该在 a/b 中使用锁定吗?锁定读写器?
->->->2.11 如果我不使用任何锁,会发生什么?读写器同时访问会抛出异常吗?
->->->2.12 如果我只是将编写器锁定在 b.ashx 中,会发生什么? a.aspx 中的阅读器会被屏蔽吗?处理这种情况的最佳做法是什么?

->->2.2 如果读写器都发生在多线程访问中。它们都是公开页面。
a.aspx -> 只是从缓存中读取
b.aspx -> 修改字典

怎么办?全部锁定?

->->2.3 如果我实现一个新的字典添加功能:
它只是将当前字典复制到新字典,然后添加新项目或修改 最后返回新的dic,它会解决并发问题吗?

哈希表会解决这些问题吗?

如何确定一个项目需要被锁定?我认为我在这些事情上错了=_=。

->3.最后一个问题..我有两个网络应用程序
a -> 显示网络
b -> 管理一些设置
他们都有自己的缓存,我可以并发两个缓存吗?
[2.1] 中的操作是否正确或其他操作?(我测试了 memcached 但它比 web 应用程序中的速度太慢,所以我只使用了两个)

感谢您阅读所有内容-_-...
希望你能知道我在说什么:)

更新======================================== ===============
感谢亚伦的回答。看完他的回答后,我试着回答自己:)
1.关于缓存,如果数据不会被修改,可以先在Application_Start(global.asax)中读入缓存。
1.1 如果锁,我应该在读取数据时添加锁开始,而不仅仅是写入。锁定项目应该是静态的,但我也对 1.1 感到不确定。
1.2 是的,如果您可以假设代码只是从数据库中读取日期然后插入缓存并且不会修改(对吗?-_-)。这个结果可能会从数据库中读取多次。我认为这不是一个大问题。

2. 泛型字典写入不是线程安全的,所以修改时要加锁。为了解决这个问题,我可以使用不可变字典。
但是如果我在.net4.0 中使用ConcurrentDictionary(读/写时线程安全)或者我自己实现一个新字典(使用读写器锁),它会解决吗?修改时需要重新锁定吗?代码就像

ConcurrentDictionary user= HttpRuntime.Cache["myCacheItem"] as ConcurrentDictionary;
if (user == null)
{ 
   //is it safe when the user is ConcurrentDictionary?
   HttpRuntime.Cache["myCacheItem"] = GetDateFromDateBase();
}
else
{
    //is it safe when the user is ConcurrentDictionary?
    user["1"] = a_new_user;
}


3.问题是,我有一个像stroe这样的小应用程序,它有两个Web应用程序一个是商店展示站点(A),另一个是管理站点(B),所以我需要并发两个Web缓存,像这样,如果我在 B 中修改产品价格,我如何通知站点 A 更改/删除缓存?(我知道 A 中的缓存可以设置 shortor,但不会很快,所以我想知道是否有内部支持 asp.net 还是像问题 2.1 一样?有一个 aspx/ashx 页面可以调用吗?)

【问题讨论】:

  • 是的,您需要同时锁定读取和写入。如果您使用ConcurrentDictionary,那么您不需要锁,但请确保在适用的情况下使用AddOrUpdate/GetOrAdd 方法。但是缓存项本身仍然存在竞争条件,这就是为什么我建议在Application_Start 中对其进行初始化,因此您永远不必检查是否为空。至于您的缓存“共享”要求,如果数据库正在更新,您可以使用SqlCacheDependency,否则您实际上是在谈论 IPC,我建议您开始一个新问题(或者更好的是,搜索现有问题) .

标签: asp.net caching dictionary thread-safety locking


【解决方案1】:

线程安全意味着您不必担心多个线程同时读取和写入同一个单独的缓存项。简单的读写操作不需要任何类型的锁。

但是,如果您尝试执行应该是原子操作 - 例如,检查项目是否存在,如果不存在则添加它 - 那么您需要同步或序列化访问。因为锁需要是全局的,所以通常最好在你的global.asax 中声明一个static readonly object 并在执行原子操作之前锁定它。请注意,您应该在 read 之前锁定并且仅在 write 之后释放锁定,因此您在上面示例中假设的锁定实际上发生得太晚了。

这就是为什么许多 Web 应用程序不延迟加载缓存的原因;相反,它们在Application_Start 方法中执行加载。至少,将一个空字典放入缓存中可以省去检查null 的麻烦,这样您就不必同步,因为您只会访问缓存项一次(但请继续阅读)。

问题的另一个方面是,即使缓存本身是线程安全的,它也不会使您添加到其中的任何项目成为线程安全的。这意味着如果您在其中存储字典,则可以保证检索缓存项的任何线程都会获得完全初始化的字典,但是如果您有多个请求尝试访问,您仍然可以创建竞争条件和其他线程安全问题字典同时至少有一个在修改它。

.NET 中的字典可以支持多个并发读取器但不支持写入器,因此如果您在 ASP.NET 缓存中存储一​​个字典并且有多个线程/请求读取和写入,那么您肯定会遇到问题。如果您可以保证只有一个线程会写入 到字典,那么您可以将字典用作不可变类型——即复制字典、修改副本并替换原来的与副本一起缓存。如果字典不经常修改,那么这确实会为您省去同步访问的麻烦,因为没有请求会尝试从正在修改的同一个字典中读取。另一方面,如果字典非常大和/或经常修改,那么在制作所有这些副本时可能会遇到性能问题 - 唯一可以确定的方法是配置文件、配置文件、配置文件!

如果您发现性能限制不允许使用该方法,那么唯一的其他选择就是同步访问。如果您知道一次只有一个线程会修改字典,那么读写器锁(即ReaderWriterLockSlim)应该适合您。如果你不能保证这一点,那么你需要一个Monitor(或者只是一个简单的lock(...)子句围绕每个操作序列)。

那应该回答问题 1-2;很抱歉,但我不太明白您在#3 中的要求。 ASP.NET 应用程序都是在它们自己的 AppDomain 中实例化的,因此实际上没有任何并发​​问题可言,因为没有共享任何内容(除非您实际上使用了某种 IPC 方法,在这种情况下,一切都是公平的游戏)。

我是否正确理解了您的问题?这有帮助吗?

【讨论】:

    猜你喜欢
    • 2012-10-25
    • 2023-04-06
    • 2011-01-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-14
    相关资源
    最近更新 更多