What is the best way to lock cache in asp.net?

前端 未结 10 677
悲&欢浪女
悲&欢浪女 2020-11-27 09:33

I know in certain circumstances, such as long running processes, it is important to lock ASP.NET cache in order to avoid subsequent requests by another user for that resourc

相关标签:
10条回答
  • 2020-11-27 09:59

    I've wrote a library that solves that particular issue: Rocks.Caching

    Also I've blogged about this problem in details and explained why it's important here.

    0 讨论(0)
  • 2020-11-27 10:08

    Here's the basic pattern:

    • Check the cache for the value, return if its available
    • If the value is not in the cache, then implement a lock
    • Inside the lock, check the cache again, you might have been blocked
    • Perform the value look up and cache it
    • Release the lock

    In code, it looks like this:

    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
    
    }
    
    0 讨论(0)
  • 2020-11-27 10:11

    Craig Shoemaker has made an excellent show on asp.net caching: http://polymorphicpodcast.com/shows/webperformance/

    0 讨论(0)
  • 2020-11-27 10:11

    I have come up with the following extension method:

    private static readonly object _lock = new object();
    
    public static TResult GetOrAdd<TResult>(this Cache cache, string key, Func<TResult> action, int duration = 300) {
        TResult result;
        var data = cache[key]; // Can't cast using as operator as TResult may be an int or bool
    
        if (data == null) {
            lock (_lock) {
                data = cache[key];
    
                if (data == null) {
                    result = action();
    
                    if (result == null)
                        return result;
    
                    if (duration > 0)
                        cache.Insert(key, result, null, DateTime.UtcNow.AddSeconds(duration), TimeSpan.Zero);
                } else
                    result = (TResult)data;
            }
        } else
            result = (TResult)data;
    
        return result;
    }
    

    I have used both @John Owen and @user378380 answers. My solution allows you to store int and bool values within the cache aswell.

    Please correct me if there's any errors or whether it can be written a little better.

    0 讨论(0)
  • 2020-11-27 10:12

    Just to echo what Pavel said, I believe this is the most thread safe way of writing it

    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;
        }
    
    0 讨论(0)
  • 2020-11-27 10:12

    I modified @user378380's code for more flexibility. Instead of returning TResult now returns object for accepting different types in order. Also adding some parameters for flexibility. All the idea belongs to @user378380.

     private static readonly object _lock = new object();
    
    
    //If getOnly is true, only get existing cache value, not updating it. If cache value is null then      set it first as running action method. So could return old value or action result value.
    //If getOnly is false, update the old value with action result. If cache value is null then      set it first as running action method. So always return action result value.
    //With oldValueReturned boolean we can cast returning object(if it is not null) appropriate type on main code.
    
    
     public static object GetOrAdd<TResult>(this Cache cache, string key, Func<TResult> action,
        DateTime absoluteExpireTime, TimeSpan slidingExpireTime, bool getOnly, out bool oldValueReturned)
    {
        object result;
        var data = cache[key]; 
    
        if (data == null)
        {
            lock (_lock)
            {
                data = cache[key];
    
                if (data == null)
                {
                    oldValueReturned = false;
                    result = action();
    
                    if (result == null)
                    {                       
                        return result;
                    }
    
                    cache.Insert(key, result, null, absoluteExpireTime, slidingExpireTime);
                }
                else
                {
                    if (getOnly)
                    {
                        oldValueReturned = true;
                        result = data;
                    }
                    else
                    {
                        oldValueReturned = false;
                        result = action();
                        if (result == null)
                        {                            
                            return result;
                        }
    
                        cache.Insert(key, result, null, absoluteExpireTime, slidingExpireTime);
                    }
                }
            }
        }
        else
        {
            if(getOnly)
            {
                oldValueReturned = true;
                result = data;
            }
            else
            {
                oldValueReturned = false;
                result = action();
                if (result == null)
                {
                    return result;
                }
    
                cache.Insert(key, result, null, absoluteExpireTime, slidingExpireTime);
            }            
        }
    
        return result;
    }
    
    0 讨论(0)
提交回复
热议问题