C#, ConcurrentDictionary.GetOrAdd – Func not exclusively accessed

This is a quick post showing you the behavior of ConcurrentDictionary.GetOrAdd, which might not be what you expect. I bet that when you first read it, you think:

If the key exists, the corresponding value is returned; if not the Func is executed “Once”, and the resulting value is associated with the key.

Are you sure about this? Lets have a look. The code below kicks of two tasks that will access GetOrAdd for the same key. I use some threading synchronization techniques to get concurrent execution of the Func. Like: SemaphoreSlim, SpinWait, Interlocked.Increment.

private static readonly StringBuilder Log = new StringBuilder();
private static readonly SemaphoreSlim Sync = new SemaphoreSlim(1, 1);
private static int _tasksStartedCount = 0;

static void Main(string[] args)
{
	var dictionary = new ConcurrentDictionary<int, string>();
	var key = 42;

	//Semaphore is used to control the flow of the tasks.
	Sync.Wait();
		
	var task1 = Task.Factory.StartNew(() =>
	{
		Thread.CurrentThread.Name = "Task1's thread";
		dictionary.GetOrAdd(key, CreateItem);
	});
	Log.AppendLine("Started Task1");

	var task2 = Task.Factory.StartNew(() =>
	{
		Thread.CurrentThread.Name = "Task2's thread";
		dictionary.GetOrAdd(key, CreateItem);
	});
	Log.AppendLine("Started Task2");

	//Wait until both tasks are waiting to return factory value
	var spinWait = new SpinWait();
	while (_tasksStartedCount < 2)
		spinWait.SpinOnce();

	Log.AppendLine("Main - Releasing Sync so that tasks can proceed");
	Sync.Release();

	Task.WaitAll(task1, task2);
	Log.AppendLine("Tasks done");

	Console.WriteLine(Log.ToString());
	Console.WriteLine("Dictionary contains string: '{0}'", 
            dictionary.First().Value);

	Console.ReadKey();
}

private static string CreateItem(int key)
{
	try
	{
		//Increase value so that Sync in Main will be released
		Interlocked.Increment(ref _tasksStartedCount);

		Log.AppendFormat("Thread '{0}' - In CreateItem, before Sync.Wait\r\n", 
                    Thread.CurrentThread.Name);
		Sync.Wait();
		Log.AppendFormat("Thread '{0}' - In CreateItem, returning value: '{0}'.\r\n", 
                    Thread.CurrentThread.Name);
			
		return Thread.CurrentThread.Name;
	}
	finally
	{
		Log.AppendFormat("Thread '{0}' - In CreateItem, before Sync.Release\r\n", 
                    Thread.CurrentThread.Name);
		Sync.Release();
		Log.AppendFormat("Thread '{0}' - In CreateItem, after Sync.Release\r\n", 
                    Thread.CurrentThread.Name);
	}
}

The output of this (with some variances) is:

Note that BOTH Funcs where executed but the first one finished was used for the value. Hence, if you pass a factory, don’t make it expensive. You could of course use it in conjunction with e.g. Lazy, that is, let the Dictionary hold a value of Lazy of T.

//Daniel