I started working with some .NET 3.5 code and found the following extension method used for the cache:
public static TValue GetOrAdd<TKey, TValue>(this Dictionary<TKey, TValue> @this, TKey key,Func<TKey,TValue> factory,bool useLocking) { TValue value; if(!@this.TryGetValue(key,out value)) { if (useLocking) { lock ((@this as ICollection).SyncRoot) { if (!@this.TryGetValue(key, out value)) { @this[key] = value = factory(key); } } } else { @this[key] = value = factory(key); } } return value; }
The cache in question is set by string keys, and using useLocking = true. This method is always accessed by this method (there is no wandering TryGetValue
). There is also no problem using the SyncRoot
property, because the dictionary is closed and the other is not used. Double locking is dangerous because the dictionary does not support reading while writing. Although the problem has not yet been technically reported since the product has not been shipped, I believe that this approach will lead to race conditions.
Switch Dictionary<,>
to Hashtable
. We will lose type safety, but we will be able to support the concurrency model that we are after (1 writer, several readers).
Remove the external TryGetValue. Thus, each read requires the acquisition of a lock. This is potentially bad for performance, but getting an unfinished lock should be pretty cheap.
Both are pretty crappy. Does anyone have a better suggestion? If it was .NET 4 code, I would just switch it to ConcurrentDictionary
, but I don't have this option.
c # concurrency
Michael b
source share