在asp.net中锁定缓存的最佳方法是什么?

Joh*_*wen 75 .net c# asp.net caching

我知道在某些情况下,例如长时间运行的进程,锁定ASP.NET缓存非常重要,以避免其他用户对该资源的后续请求再次执行长进程而不是命中缓存.

在ASP.NET中实现缓存锁定的最佳方法是什么?

a7d*_*rew 110

这是基本模式:

  • 检查缓存中的值,如果可用则返回
  • 如果该值不在缓存中,则实现锁定
  • 在锁内,再次检查缓存,您可能已被阻止
  • 执行值查找并缓存它
  • 解锁

在代码中,它看起来像这样:

private static object ThisLock = new object();

public string GetFoo()
{

  // try to pull from cache here

  lock (ThisLock)
  {
    // cache was empty before we got the lock, check again inside the lock

    // cache is still empty, so retreive the value here

    // store the value in the cache here
  }

  // return the cached value here

}
Run Code Online (Sandbox Code Playgroud)

  • 被称为"双重锁定".http://en.wikipedia.org/wiki/Double-checked_locking (5认同)
  • 如果缓存的第一次加载需要几分钟,是否还有办法访问已加载的条目?让我们说如果我有GetFoo_AmazonArticlesByCategory(string categoryKey).我猜它会像每个类别的锁一样锁定. (4认同)

Joh*_*wen 31

为了完整性,一个完整的例子看起来像这样.

private static object ThisLock = new object();
...
object dataObject = Cache["globalData"];
if( dataObject == null )
{
    lock( ThisLock )
    {
        dataObject = Cache["globalData"];

        if( dataObject == null )
        {
            //Get Data from db
             dataObject = GlobalObj.GetData();
             Cache["globalData"] = dataObject;
        }
    }
}
return dataObject;
Run Code Online (Sandbox Code Playgroud)

  • @Constantin:不是真的,有人可能在你等待获取锁时更新了缓存() (28认同)
  • @John Owen - 在锁定语句之后你必须再次尝试从缓存中获取对象! (12认同)
  • 这段代码实际上仍然是错误的.你在一个实际上不存在的范围内返回`globalObject`.应该发生的是`dataObject`应该在最终的null检查中使用,而globalObject根本不需要存在事件. (11认同)
  • if(dataObject == null){lock(ThisLock){if(dataObject == null)//当然它仍为null! (7认同)
  • -1,代码错误(阅读其他评论),为什么不修复它?人们可能会尝试使用您的示例. (3认同)

cwi*_*lls 14

无需锁定整个缓存实例,而只需要锁定要插入的特定密钥.也就是说,当你使用男厕时,不需要阻止进入女厕所:)

下面的实现允许使用并发字典锁定特定的高速缓存密钥.这样,您可以同时为两个不同的键运行GetOrAdd() - 但不能同时为同一个键运行.

using System;
using System.Collections.Concurrent;
using System.Web.Caching;

public static class CacheExtensions
{
    private static ConcurrentDictionary<string, object> keyLocks = new ConcurrentDictionary<string, object>();

    /// <summary>
    /// Get or Add the item to the cache using the given key. Lazily executes the value factory only if/when needed
    /// </summary>
    public static T GetOrAdd<T>(this Cache cache, string key, int durationInSeconds, Func<T> factory)
        where T : class
    {
        // Try and get value from the cache
        var value = cache.Get(key);
        if (value == null)
        {
            // If not yet cached, lock the key value and add to cache
            lock (keyLocks.GetOrAdd(key, new object()))
            {
                // Try and get from cache again in case it has been added in the meantime
                value = cache.Get(key);
                if (value == null && (value = factory()) != null)
                {
                    // TODO: Some of these parameters could be added to method signature later if required
                    cache.Insert(
                        key: key,
                        value: value,
                        dependencies: null,
                        absoluteExpiration: DateTime.Now.AddSeconds(durationInSeconds),
                        slidingExpiration: Cache.NoSlidingExpiration,
                        priority: CacheItemPriority.Default,
                        onRemoveCallback: null);
                }

                // Remove temporary key lock
                keyLocks.TryRemove(key, out object locker);
            }
        }

        return value as T;
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 这很棒.锁定缓存的重点是避免重复完成工作以获取该特定密钥的值.按类锁定整个缓存甚至部分都是愚蠢的.你想要的确是这样 - 一个锁定说"我正在获得<key>的价值,其他人都在等我." 扩展方法也很光滑.一个好主意!这应该是人们发现的答案.谢谢. (2认同)

use*_*380 13

为了回应Pavel所说的,我相信这是编写它的最安全的方式

private T GetOrAddToCache<T>(string cacheKey, GenericObjectParamsDelegate<T> creator, params object[] creatorArgs) where T : class, new()
    {
        T returnValue = HttpContext.Current.Cache[cacheKey] as T;
        if (returnValue == null)
        {
            lock (this)
            {
                returnValue = HttpContext.Current.Cache[cacheKey] as T;
                if (returnValue == null)
                {
                    returnValue = creator(creatorArgs);
                    if (returnValue == null)
                    {
                        throw new Exception("Attempt to cache a null reference");
                    }
                    HttpContext.Current.Cache.Add(
                        cacheKey,
                        returnValue,
                        null,
                        System.Web.Caching.Cache.NoAbsoluteExpiration,
                        System.Web.Caching.Cache.NoSlidingExpiration,
                        CacheItemPriority.Normal,
                        null);
                }
            }
        }

        return returnValue;
    }
Run Code Online (Sandbox Code Playgroud)

  • ['lock(this)`是*********](http://stackoverflow.com/questions/251391/why-is-lockthis-bad).您应该使用类的用户不可见的专用锁定对象.假设,有人决定使用缓存对象来锁定.他们将不知道它被内部用于锁定目的,这可能导致不良. (7认同)