I have the following test:
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading.Tasks;
public class Program
{
public static void Main()
{
IDictionary<string, string> dictionary = new ConcurrentDictionary<string, string>(); // FAILS sometimes
// ConcurrentDictionary<string, string> dictionary = new ConcurrentDictionary<string, string>(); // WORKS always
Parallel.For(0, 10, i => dictionary.TryAdd("x1", "y1"));
}
}
If I run it in .NET Fiddle, I sometimes get the following exception:
Unhandled exception. System.AggregateException: One or more errors occurred. (The key already existed in the dictionary)
System.ArgumentException: The key already existed in the dictionary.at System.Collections.Concurrent.ConcurrentDictionary`2.System.Collections.Generic.IDictionary<TKey,TValue>.Add(TKey key, TValue value)
at System.Collections.Generic.CollectionExtensions.TryAdd[TKey,TValue](IDictionary`2 dictionary, TKey key, TValue value)
at Program.<>c__DisplayClass0_0.b__0(Int32 i)
at System.Threading.Tasks.Parallel.<>c__DisplayClass19_0`2.b__1(RangeWorker& currentWorker, Int64 timeout, Boolean& replicationDelegateYieldedBeforeCompletion)
--- End of stack trace from previous location ---
at System.Threading.Tasks.Parallel.<>c__DisplayClass19_0`2.b__1(RangeWorker& currentWorker, Int64 timeout, Boolean& replicationDelegateYieldedBeforeCompletion)
at System.Threading.Tasks.TaskReplicator.Replica.Execute()
--- End of inner exception stack trace ---
However, if I don't cast ConcurrentDictionary
, it seems to always work.
What's going on?
This is actually covered in the
ConcurrentDictionary<TKey,TValue>
docs in the "Thread Safety" section which is hidden that far away so almost everybody misses it (emphasis mine):And the
IDictionary<string, string> dictionary = ..
falls under the extensions methods category, specificallyCollectionExtensions.TryAdd<TKey,TValue>(IDictionary<TKey,TValue> dictionary, ...)
So in short - if you need to use the dictionary in thread-safe manner do not use it via interfaces/extension methods and use the methods listed in the Remarks section: