In C# 7, I am making a call to a concurrent dictionary with "GetOrAdd". I have a composite key, say something like "Key1,Part-key1". Inside the value factory I can retrieve the whole tree under the "Key1", meaning that I can load the rest of the "part-keyN" keys. I am doing that because the consumer of the dictionary only require the part-key but when loading it remotely I get the whole document, so I'm optimistically loading it for subsequent calls.
My question is, if I update other keys inside the ConcurrentDictionary, would there be a time in which I might end up in a deadlock or something similar?
var cd = new ConcurrentDictionary<Tuple<string,string>,Task<string>>();
cd.GetOrAdd(Tuple.Create("Key1","Part1"), async (k)=>{
var fullObject = await retrieveObject(k.Item1);
foreach(var prop in fullObject.Properties){
cd.TryAdd(Tuple.Create(k.Item1,prop.Key),prop.Value);
}
return fullObject.Properties.Find(prop => prop.Key == k.Item2).Value;
});
A Composite key for me its just a tuple that goes in the key field of the dictionary.
No, there is no risk of a deadlock. The risk is adding inconsistent entries in the dictionary.
There is no guarantee that the
valueFactorywill be called only once per key. In case two threads callGetOrAddconcurrently, each thread will invoke independently thevalueFactory, and each thread will produce a differentTValue. Only one of theTValues will be added in the dictionary though. BothGetOrAddcalls will return the same value, which will be the value that won the race to be added in the dictionary. The other value will be silently discarded.In your case the
valueFactoryhas side-effects, and these side-effects will persist for all invocations, even for those that will lose the race to add their result in the dictionary. The side-effects are the entries that are added in the dictionary with theTryAdd. Let's imagine that thefullObjects returned by the two concurrent invocations are not identical. They may have different number ofProperties, and the common properties might have different values. It is possible that only half of eachfullObject's properties will be added in the dictionary, because they will be racing to overwrite each other's properties. Most likely this is going to emerge as a bug in your application, in one way or another.In case you are interested for
GetOrAddAsyncimplementations that guarantee that thevalueFactoryis invoked only once per key, you could look at this question: ConcurrentDictionary GetOrAdd async.