The extension method:
/// <summary>
/// Thread safe. May lock on the input dictionary.
/// </summary>
public static U GetOrAdd<T, U>(this Dictionary<T, U> dict, T key, Func<U> create) {
U val;
if (!dict.TryGetValue(key, out val)) {
lock (dict) {
if (!dict.ContainsKey(key)) {
val = create();
dict[key] = val;
} else {
val = dict[key];
}
}
}
return val;
}
Sample usage:
var idVals = new Dictionary<int, List<int>>();
List<int> vals = idVals.GetOrAdd(id, () => new List<int>());
vals.Add(3);
Explanation:
- If the key exists, we quickly retrieve the value using the
TryGetValue()
method - If the key doesn't exist we use the provided
create
delegate to make a new one - We are protected against the case of two threads simultaneously looking for a key that doesn't exist by locking on the input dictionary and afterward re-checking to see if the key still doesn't exist
Questions:
- Are there scenarios that will cause this fail?
- I found this extension method to be useful, but is clear enough to be properly understood by others?
- Are there easier ways to achieve the thread-safety I'm looking for?
- I know it's considered dangerous and bad practice to lock on an input parameter, but can I rely on the fact that locking on a dictionary to safely add/remove elements is a common convention?
ReaderWriterLock
to prevent reads during writes. \$\endgroup\$