Tom Gullen Tom Gullen - 19 days ago 8
C# Question

Can an object in a cache be removed during a function call within that object?

Given the code:

private static readonly object GetMyObjLock = new object();
public static MyObject GetFromCache()
{
var key = "MyObj";
var cache = HttpContext.Current.Cache;
if (cache[key] == null)
{
lock (GetMyObjLock)
{
if (cache[key] == null)
{
cache.Add(key,
new MyObject(), null,
Cache.NoAbsoluteExpiration, Cache.NoSlidingExpiration,
CacheItemPriority.Normal, null);
}
}
}
return (MyObject) cache[key];
}


And the call:

var myObj = GetFromCache();
myObj.PerformMethod();


With some code listening on another thread that does:

if(message == "invalidate")
{
Httpcontext.Current.Cache.Remove("MyObj");
}


Two questions:


  • Will
    PerformMethod
    always complete?

  • Is there any opportunity for
    myObj
    to equal
    null
    between the
    GetFromCache()
    method and
    PerformMethod
    call?


Answer

The way to fix it is use the output of the Add function to see if the item you added was actually added to the cache, if it already exists it will return the previous instance, if it did not exist it returns null. When it returns null you just set the object you passed in as the 2nd parameter as your result to return from the function.

private static readonly object GetMyObjLock = new object();
public static MyObject GetFromCache()
{
    var key = "MyObj";
    var cache = HttpContext.Current.Cache;
    MyObject result;

    //Try to pull from the cache.
    result = (MyObject)cache[key];
    if(result == null)
    {
        //This lock is used to make sure the MyObject constructor is not called 
        // concurrently, it is not for thread safe access to the cache.
        lock(GetMyObjLock)
        {
            //Check to see if anyone made a new object while we where waiting.
            result = (MyObject)cache[key];
            if(result == null)
            {
                var newObject = new MyObject();

                //Try to add to cache, if the object was already added by someone who 
                // does not use the GetMyObjLock this function returns the previously 
                // added instance.
                result = (MyObject)cache.Add(key,
                    newObject, null,
                    Cache.NoAbsoluteExpiration, Cache.NoSlidingExpiration,
                    CacheItemPriority.Normal, null);

                //If result is null then we successfully added a new 
                // item, assign it to result.
                if(result == null)
                    result = newObject;
                }
            }
        }
    }        
    return result;
}
Comments