by Damiaan Peeters
13. August 2013 14:52
I was looking at some code from Zack Owens “Multi Tenancy Sample”. He has some clever code, a ThreadSafeDictionary with a GetOrAdd method. I love the idea! (code below for my own reference)
The problem with a dictionary, is that – apparently - .Net puts somewhere a system wide lock to ensure thread safety on a dictionary anyway. That’s the reason that, if you are working with huge dictionaries with lots of READS, you might bump into some performance issues. In such cases, you could (and should) use the ConcurrentDictionary from the System.Collections.Concurrent namespace. And if you are looking at this class, visit the BlockingCollection from the same namespace also, still trying to wrap my head around it, but it is ultra important. You just feel it when reading through the MSDN article.
/// DON’T USE THIS CODE – You probably want a System.Collections.Concurrent.ConcurrentDictionary
using System;
using System.Collections.Generic;
using System.Threading;
/// <summary>
/// Dictionary that has a "GetOrAdd" method that is thread-safe
/// </summary>
/// <typeparam name="TKey">Dictionary key</typeparam>
/// <typeparam name="TValue">Dictionary value</typeparam>
public class ThreadSafeDictionary<TKey, TValue> : Dictionary<TKey, TValue>
{
/// <summary>
/// Lock for adding values
/// </summary>
private ReaderWriterLockSlim cacheLock = new ReaderWriterLockSlim();
/// <summary>
/// Gets a value or adds the value in a thread-safe way
/// </summary>
/// <param name="key">Key in the dictionary</param>
/// <param name="defaultValue">Delegate that will get the value</param>
/// <returns>Value from the dictionary with given <paramref name="key"/></returns>
public TValue GetOrAdd(TKey key, Func<TValue> defaultValue)
{
// enter read lock
this.cacheLock.EnterReadLock();
try
{
// test if value is in the dictionary
if (this.ContainsKey(key))
return this[key];
}
finally
{
// exit read lock
this.cacheLock.ExitReadLock();
}
// enter write lock
this.cacheLock.EnterWriteLock();
try
{
if (!ContainsKey(key))
this.Add(key, defaultValue());
return this[key];
}
finally
{
// exit write lock
this.cacheLock.ExitWriteLock();
}
}
}