安全地使用'HttpContext.Current.Cache'

时间:2010-04-14 14:47:45

标签: c# multithreading caching service thread-safety

我在这样的网络服务方法中使用Cache

var pblDataList = (List<blabla>)HttpContext.Current.Cache.Get("pblDataList");

if (pblDataList == null)
{
    var PBLData = dc.ExecuteQuery<blabla>(@"SELECT blabla");

    pblDataList = PBLData.ToList();

    HttpContext.Current.Cache.Add("pblDataList", pblDataList, null,
        DateTime.Now.Add(new TimeSpan(0, 0, 15)),
        Cache.NoSlidingExpiration, CacheItemPriority.Normal, null);
}

但我想知道,这段代码是否是线程安全的? Web服务方法由多个请求者调用。当缓存为空时,多个请求者可能会尝试检索数据并同时添加到Cache

查询需要5到8秒。是否会围绕此代码引入锁定语句以防止任何可能的冲突? (我知道多个查询可以同时运行,但我想确保一次只运行一个查询。)

3 个答案:

答案 0 :(得分:22)

缓存对象是线程安全的,但HttpContext.Current将无法从后台线程获得。这可能适用于您,也可能不适用于此,从您的代码段开始,无论是否或不是你实际上是在使用后台线程,但是如果你现在或将来某个时候做出决定,你应该牢记这一点。

如果您有可能需要从后台线程访问缓存,请改用HttpRuntime.Cache

此外,虽然缓存上的单个操作是线程安全的,但顺序查找/存储操作显然不是原子操作。您是否需要它们是原子的取决于您的特定应用程序。如果同一个查询多次运行可能是一个严重的问题,即它是否会产生比数据库能够处理的更多的负载,或者如果请求返回的数据立即被覆盖,则会出现问题缓存,那么你可能想要锁定整个代码块。

但是,在大多数情况下,您确实希望首先进行分析,然后查看这是否真的存在问题。大多数Web应用程序/服务都不关心缓存的这一方面,因为它们是无状态的,并且缓存是否被覆盖无关紧要。

答案 1 :(得分:2)

你是对的。检索和添加操作不会被视为原子事务。如果您需要阻止查询多次运行,则需要使用锁定。

(通常情况下这不是什么大问题,但是对于长时间运行的查询,可以减轻数据库的压力。)

答案 2 :(得分:1)

我相信Add应该是线程安全的 - 即如果使用相同的密钥调用Add两次就不会出错,但显然查询可能执行两次。

然而,另一个问题是是数据线程安全的。无法保证每个List<blabla>都是隔离的 - 它取决于缓存提供者。内存缓存提供程序直接存储对象,因此如果任何线程编辑数据(在列表中添加/删除/交换项目,或更改其中一个项目的属性),则存在冲突风险。但是,使用序列化提供程序,您应该没问题。当然,这要求blabla可序列化......