It’s been a while since my last post. I’ve got ported to web development, language changed to C#, and although switching from thick client to web apps is like learning to walk again I think I’m starting to like it. But let’s get started.
In vanilla times of .Net there was Hashtable to store key-value pairs, and there was Synchronized Hashtable if you wanted to bother the elements from more than one thread at the same time. Atomic operations like Add/Remove/Contains have been synced internally, while it provided it’s lock object as a public property in case you wanted to tinker with the enumerator, while being sure no one accidentally removes the element you are looking for. With the introduction of Generics – and Generic Dictionaries – Microsoft discontinued the support of synced key-value pair collections. Some say it’s because foreach loop is not automatically wrapped safe, others say there is absolutely no use of them. I’d say the lack of understanding and thus the lack of usage was the main reason. Most programmers are afraid of multi threading, and don’t even care about divergent thinking, nor how it inspired the idea of synchronous tasks in modern coding aspects.
I was in great need of a strong typed key-value collection, that can be accessed from multiple threads at the same time without littering my code with locks in every second row. Actually I was building a custom cache for an ASP.Net application and I wanted to run custom queries against the cache table itself by writing some LINQ queries, but hell knows why, the Hashtable/typecasting/LINQ trio gave me the creeps.
The solution is rather simple. I did not rewrite Dictionary from scratch – building custom cache in ASP.Net is more than enough from reinventing the wheel – instead I simply wrapped a normal dictionary into an IDictionary implementation with locks on the proxy method calls. Here goes the code, speaks for itself, so no comments today:
using System; using System.Collections; using System.Collections.Generic; public class SynchronizedDictionary<TKey, TValue> : IDictionary<TKey, TValue> { private Dictionary<TKey, TValue> _innerDict; private readonly Object _syncRoot = new object(); public object SyncRoot { get { return _syncRoot; } } #region IDictionary<TKey,TValue> Members public void Add(TKey key, TValue value) { lock (_syncRoot) { _innerDict.Add(key, value); } } public bool ContainsKey(TKey key) { lock (_syncRoot) { return _innerDict.ContainsKey(key); } } public ICollection<TKey> Keys { get { lock (_syncRoot) { return _innerDict.Keys; } } } public bool Remove(TKey key) { lock (_syncRoot) { return _innerDict.Remove(key); } } public bool TryGetValue(TKey key, out TValue value) { lock (_syncRoot) { return _innerDict.TryGetValue(key, out value); } } public ICollection<TValue> Values { get { lock (_syncRoot) { return _innerDict.Values; } } } public TValue this[TKey key] { get { lock (_syncRoot) { return _innerDict[key]; } } set { lock (_syncRoot) { _innerDict[key] = value; } } } #endregion #region ICollection<KeyValuePair<TKey,TValue>> Members public void Add(KeyValuePair<TKey, TValue> item) { lock (_syncRoot) { (_innerDict as ICollection<KeyValuePair<TKey, TValue>>).Add(item); } } public void Clear() { lock (_syncRoot) { _innerDict.Clear(); } } public bool Contains(KeyValuePair<TKey, TValue> item) { lock (_syncRoot) { return (_innerDict as ICollection<KeyValuePair<TKey, TValue>>).Contains(item); } } public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex) { lock (_syncRoot) { (_innerDict as ICollection<KeyValuePair<TKey, TValue>>).CopyTo(array, arrayIndex); } } public int Count { get { lock (_syncRoot) { return _innerDict.Count; } } } public bool IsReadOnly { get { return false; } } public bool Remove(KeyValuePair<TKey, TValue> item) { lock (_syncRoot) { return (_innerDict as ICollection<KeyValuePair<TKey, TValue>>).Remove(item); } } #endregion #region IEnumerable<KeyValuePair<TKey,TValue>> Members public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() { return _innerDict.GetEnumerator(); } #endregion #region IEnumerable Members IEnumerator IEnumerable.GetEnumerator() { return _innerDict.GetEnumerator(); } #endregion public SynchronizedDictionary() { _innerDict = new Dictionary<TKey, TValue>(); } }
And a simple example of usage should clear up the questions:
static GenericSyncedDictionary<string, object> SynchronizedDictionary= new SynchronizedDictionary<string, object>(); public static string AddObject(object value) { string id = new Guid().ToString(); //No locking needed, as the wrapper locks for add syncedDict.Add(id, value); return id; } public static object[] SelectCahcedObjects() { object[] returnValue; //Ensures that no one tinkers with the dictionary, while the linq query executes lock (syncedDict.SyncRoot) { var query = from o in syncedDict where o.Value.GetHashCode() > 42 select o.Value; returnValue = query.ToArray(); } return returnValue; }