I'm writing a portable class library that targets .NET 4.5, Windows Store apps and Windows Phone 8. I need an efficient in-memory cache mechanism, so I was thinking about using ConcurrentDictionary<K,V>
, but it's not available in WP8.
There will be many reads and relatively few writes, so ideally I'd like a collection that supports lock-free reads from multiple threads, and write by a single thread. The non-generic Hashtable
has that property, according to MSDN, but unfortunately it's not available in the PCL...
Is there another collection class available in the PCL that matches this requirement? If not, what would be a good way to achieve thread safety without locking for reads? (locking for writes is OK, since it won't happen too often)
EDIT: thanks to JaredPar's guidance, I eventually implemented my cache in a completely lock-free fashion, using ImmutableDictionary<TKey, TValue>
from Microsoft.Bcl.Immutable:
class Cache<TKey, TValue>
{
private IImmutableDictionary<TKey, TValue> _cache = ImmutableDictionary.Create<TKey, TValue>();
public TValue GetOrAdd(TKey key, [NotNull] Func<TKey, TValue> valueFactory)
{
valueFactory.CheckArgumentNull("valueFactory");
TValue newValue = default(TValue);
bool newValueCreated = false;
while (true)
{
var oldCache = _cache;
TValue value;
if (oldCache.TryGetValue(key, out value))
return value;
// Value not found; create it if necessary
if (!newValueCreated)
{
newValue = valueFactory(key);
newValueCreated = true;
}
// Add the new value to the cache
var newCache = oldCache.Add(key, newValue);
if (Interlocked.CompareExchange(ref _cache, newCache, oldCache) == oldCache)
{
// Cache successfully written
return newValue;
}
// Failed to write the new cache because another thread
// already changed it; try again.
}
}
public void Clear()
{
_cache = _cache.Clear();
}
}
One option to consider is to write a thin facade over an immutable search tree. There are several immutable search trees available on the web to choose from. I usually base mine off of Eric Lipperts great post on the subject
Using this as the backing data structure will give you lock free. Writes to the tree can be done in a lock free fashion with CAS as well. This will be a bit slower than
ConcurrentDictionary
because lookups are O(Log(N)) instead of approaching O(1). But it should do the trick for you