1

我正在使用下面的代码,对我来说,下面的代码似乎永远不会发生竞争条件。还是有可能出现比赛条件?

        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);
        }

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

为了完全避免任何竞争条件,我必须在真实部分中添加一个检查,否则我可能会得到一个计数为零的列表,如果其他人在缓存中清除它(不删除该项目,而只需调用 Clear 方法在我的 if 评估为 TRUE 之后,列出缓存中的对象)。那么,在 listFromCache 对象的 if 的真实部分中,我不会有任何数据。

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

此外,正如 Chris 所说,如果其他人通过调用 Cache.Remove 方法从 Cache 中“删除”项目,则 listFromCache 不会受到影响,因为垃圾收集器不会从 HEAP 内存中删除实际的 List 对象,因为名为 ' listFromCache' 仍然有它的引用(我在克里斯的回答帖子下的评论中更详细地解释了这一点)。

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);
    }
4

1 回答 1

1

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

您可以尝试使用静态对象锁定它,但老实说,我不确定这是否适用于 ASP.NET 上下文。我不记得缓存是在所有 ASP.NET 进程(IIRC,具有不同的静态上下文)之间共享还是仅在每个 Web 工作者之间共享。如果是后者,静态锁就可以正常工作。

也只是为了证明:

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
    }
}
于 2012-12-01T20:55:17.603 回答