1
\$\begingroup\$

I have written a caching wrapper method for some of my services. The actual wrapper method is written as follows:

public T GetFromCache<T>(string key, Func<T> defaultValuePredicate, object cacheLock, TimeSpan duration)
{
 lock (cacheLock)
 {
 var result = _cache[key] is T ? (T)_cache[key] : default(T);
 if (Equals(default(T), result))
 {
 result = defaultValuePredicate();
 if (!Equals(default(T), result))
 {
 _cache.Add(key, result, null,
 DateTime.Now.Add(duration),
 System.Web.Caching.Cache.NoSlidingExpiration,
 System.Web.Caching.CacheItemPriority.Default,
 (cacheKey, value, reason) =>
 Debug.WriteLine(string.Format("{0} Dropped from cache becasue {1}", cacheKey, reason)));
 }
 }
 return result;
 }
}

A typical use case is this:

public class SomeService
{
 private static readonly object _lock = new object();
 public string GetSomeValue()
 {
 return GetFromCache("SomeService_GetSomeValue", () => 
 {
 //Expensive operation here
 }, 
 _lock, 
 TimeSpan.FromSeconds(600));
 }
}

In all the example code that I've seen floating around the web people never seem to lock around expensive, but cached operations. The reason I lock is to prevent the same operation kicking off twice, duplicating the effort.

Is this approach reasonable and robust, or does it need serious modification to make it more reliable?

Jamal
35.2k13 gold badges134 silver badges238 bronze badges
asked Nov 14, 2012 at 15:43
\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

I see a minor issues with the code: you are locking every time, no matter if it's needed on not. I would implemented GetFromCache like this

public T GetFromCache<T>(string key, object cacheLock, Func<T> defaultValuePredicate, TimeSpan duration)
{
 object result = _cache[key]; // using object as T may be value type and _cache may return null
 if (result == null || !(result is T))
 {
 lock (cacheLock)
 {
 result = _cache[key]; // rechecking value from cache as another thread may already initialize it
 if (result == null || !(result is T))
 {
 result = defaultValuePredicate();
 if (!Equals(default(T), result))
 {
 _cache.Add(key, result, null,
 DateTime.Now.Add(duration),
 System.Web.Caching.Cache.NoSlidingExpiration,
 System.Web.Caching.CacheItemPriority.Default,
 (cacheKey, value, reason) =>
 Debug.WriteLine(string.Format("{0} Dropped from cache becasue {1}", cacheKey, reason)));
 }
 }
 }
 }
 return (T)result;
}

It's a little more code (+1 if statement) but it doesn't lock thread every time you access it.

answered Nov 14, 2012 at 23:32
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.