I've read this question, but I still don't understand how GetOrAdd works if I have a nested collection.
Is ConcurrentDictionary.GetOrAdd truly thread-safe?
If I haven't created a nested collection yet, and GetOrAdd is called simultaneously from multiple threads, what happens?
I expect the behavior that only one of the threads will be able to create a new collection, and the rest will receive the one already created and add a value there. Is this how it's going to happen?
var completions = subscribers.GetOrAdd(key, _ => new ConcurrentBag<TaskCompletionSource<string>>());
completions.Add(completion);
Not exactly. It is entirely possible that more than one threads will create a new
TValue, but only one of theseTValues will be added in the dictionary. The otherTValues will fall out of scope and will be discarded (garbage-collected).Yes, all threads will interact with the same
TValue. TheGetOrAddreturns theTValuecreated by the thread that won the optimistic locking race. The race is optimistic because thevalueFactorydelegate is called outside the locks used internally by theConcurrentDictionary<TKey,TValue>collection. This collection doesn't allow pessimistic locking, i.e. you can't use its internal locks for protecting your own code. Pessimistic locking could be beneficial in some scenarios, but this feature is simply not supported by this collection.