I've been researching how to implement basic memory caching in my class library. I came across this example and decided to implement something similar, but I've come across some problems:
- Is
MemoryCache
really thread-safe? Sifting through the comments, I've found people saying it is and some people saying it isn't. I've never used this class before, so I'm not certain which comments apply to my situation. - If
MemoryCache
is not thread-safe, how does one make it thread-safe? Or should I be using something different for my situation?
In my case, consuming applications will have one instance of my class (DataProviderDecorator
) used by 1-to-N threads. Will it be safe to use in that manner?:
public class DataProviderDecorator : IDataProvider
{
private readonly IDataProvider Decoratee;
private readonly MemoryCache Cache; // System.Runtime.Caching.MemoryCache
private ConcurrentDictionary<string, SemaphoreSlim> Locks { get; set; } // System.Collections.Concurrent.ConcurrentDictionary and System.Threading.SemaphoreSlim
public object GetItem(string key)
{
object result = null;
if (Cache.Contains(key))
{
return Cache.Get(key);
}
SemaphoreSlim cacheLock = Locks.GetOrAdd(key, new SemaphoreSlim(1, 1));
cacheLock.Wait();
try
{
if (Cache.Contains(key))
{
return (QueryResult)Cache.Get(key);
}
result = Decoratee.GetItem(key);
CacheItemPolicy cacheItemPolicy = new CacheItemPolicy();
cacheItemPolicy.SlidingExpiration = new TimeSpan(0, 0, 30);
if (!Cache.Add(key, result, cacheItemPolicy))
{
throw new Exception($"A record for the key [{key}] is already present in the cache.");
}
}
finally
{
cacheLock.Release();
}
return result;
}
}
Also, does anyone have any idea for how I should maintain the collection of locks? Presumably it would fill up indefinitely.