访问ASP.Net缓存项时是否会导致RACE CONDITION?

时间:2012-12-01 20:37:27

标签: c# asp.net caching

我正在使用以下代码,对我来说,似乎下面的代码不会发生竞争条件。或者仍然存在竞争条件的可能性?

        List<Document> listFromCache = Cache[dataCacheName] as List<Document>;
        if (listFromCache != null)
        {
           //do something with listFromCache. **IS IT POSSIBLE** that listFromCache is 
                                              //NULL here
        }
        else
        {
             List<Document> list = ABC.DataLayer.GetDocuments();
             Cache.Insert(dataCacheName, list, null, DateTime.Now.AddMinutes(5), 
                          System.Web.Caching.Cache.NoSlidingExpiration);
        }

更新 克里斯帮助我解决了这个问题,但我想,我会分享一些对其他人非常有帮助的细节。

为了完全避免任何竞争条件,我必须在真实部分中添加一个检查,否则我最终会得到一个零计数的List,如果有人在Cache中清除它(不是删除该项目,而只是调用Clear在我的if评估为TRUE之后,在Cache中的List对象上的方法。那么,我在listFromCache对象中的if部分中没有任何数据。

要在我的原始代码中克服这种微妙的RACE条件,我必须在下面的代码中检查真实部分中的listFromCache,然后使用最新数据重新填充Cache。

另外,正如Chris所说,如果其他人通过调用Cache.Remove,方法从Cache中删除了这些项目,那么listFromCache就不会受到影响,因为垃圾收集器不会删除实际从HEAP内存中列出对象,因为名为'listFromCache'的变量仍然具有对它的引用(我在Chris的答案帖子中的评论中对此进行了更详细的解释)。

List<Document> listFromCache = Cache[dataCacheName] as List<Document>;
    if (listFromCache != null)
    {
      //OVERCOME A SUBTLE RACE CONDITION BY IF BELOW
      if( listFromCache == null || listFromCache.Count == 0)
      {
          List<Document> list = ABC.DataLayer.GetDocuments();
          Cache.Insert(dataCacheName, list, null, DateTime.Now.AddMinutes(5), 
                      System.Web.Caching.Cache.NoSlidingExpiration);
       }
       //NOW I AM SURE MY listFromCache contains true data
       //do something with listFromCache. **IS IT POSSIBLE** that listFromCache is 
                                          //NULL here
    }
    else
    {
         List<Document> list = ABC.DataLayer.GetDocuments();
         Cache.Insert(dataCacheName, list, null, DateTime.Now.AddMinutes(5), 
                      System.Web.Caching.Cache.NoSlidingExpiration);
    }

1 个答案:

答案 0 :(得分:1)

不,您的评论中不可能listFromCache将变为空,因为它是此时的本地参考。如果缓存条目在其他位置无效,则不会影响本地引用。但是,您可能会遇到检索空值的情况,但在收集文档(ABC.DataLayer.GetDocuments())的过程中,另一个进程已经这样做并插入了缓存条目,此时您将覆盖它。 (这可能是完全可以接受的,在这种情况下,太棒了!)

您可以尝试使用静态对象锁定它,但老实说,我不确定它是否可以在ASP.NET上下文中运行。我不记得Cache是​​否在所有ASP.NET进程(IIRC,具有不同的静态上下文)之间共享,或者只在每个单独的Web worker中共享。如果是后者,静态锁定将正常工作。

也是为了证明:

List<Document> listFromCache = Cache[dataCacheName] as List<Document>;
if (listFromCache != null)
{
    Cache.Remove(dataCacheName);
    //listFromCache will NOT be null here.
    if (listFromCache != null)
    {
        Console.WriteLine("Not null!"); //this will run because it's not null
    }
}