Using a thread-safe dictionary in .NET C# Part 3: thread-safe modifications

In the previous post we looked at the 4 Try methods of ConcurrentDictionary that support CRUD operations: retrieval, deletion, update and insertion. We saw some basic examples for their usage and concluded that TryUpdate was not a very good solution to actually update an item due to race conditions.

This is where another method called AddOrUpdate enters the scene.

As the method name implies it can both update an existing value or add a new key-value pair if the key doesn’t exist yet. There are similar operations in the database world. It’s a mixture of UPDATE and INSERT called an UPSERT. AddOrUpdate is the ConcurrentDictionary equivalent of an UPSERT.

AddOrUpdate requires 3 parameters:

  • The key
  • The value if there’s no element by that key. In this case a new key-value pair will be added to the dictionary
  • A function which will calculate the new value if an element by that key already exists. This is the update part of the operation

The update function – or delegate – in turn has the following requirements:

  • Its signature must accept a key and a value whose types correspond to the key and value type of the ConcurrentDictionary
  • It must return a value of the same type that the ConcurrentDictionary requires

If you’re new to delegates, functions and lambdas then they may look very cryptic at first. You can start your investigation here.

The update function provides a mechanism to calculate the new value based on the key and the existing value. It can be as simple or complex as you need. It always returns the value that was either added in case of a new item or the updated value if it’s an update.

Here’s an example of the AddOrUpdate method:

public void RunAddOrUpdateSample()
{
	ConcurrentDictionary<string, int> movieCategoriesOnStock = new ConcurrentDictionary<string, int>();
	movieCategoriesOnStock.TryAdd("Romance", 12);
	movieCategoriesOnStock.TryAdd("Action", 9);
	movieCategoriesOnStock.TryAdd("Comedy", 20);
	Debug.WriteLine("Dictionary content before AddOrUpdate:");
	foreach (var kvp in movieCategoriesOnStock)
	{
		Debug.WriteLine(string.Format("{0}: {1}", kvp.Key, kvp.Value));
	}

	int zombie = movieCategoriesOnStock.AddOrUpdate("Zombie", 10, (key, value) => { return value * 2; });
	Debug.WriteLine("Zombie: {0}", zombie);

	int romance = movieCategoriesOnStock.AddOrUpdate("Romance", 5, (key, value) => { return CalculateNewValue(key, value); });
	Debug.WriteLine("Romance: {0}", romance);

	Debug.WriteLine("Dictionary content after AddOrUpdate:");
	foreach (var kvp in movieCategoriesOnStock)
	{
		Debug.WriteLine(string.Format("{0}: {1}", kvp.Key, kvp.Value));
	}
}

private int CalculateNewValue(string key, int value)
{
	return key.GetHashCode() + value * 3;
}

This produces the following output:

Dictionary content before AddOrUpdate:
Action: 9
Romance: 12
Comedy: 20
Zombie: 10
Romance: -1978843422
Dictionary content after AddOrUpdate:
Action: 9
Romance: -1978843422
Zombie: 10
Comedy: 20

Romance got a crazy value but I just wanted to take an example where both the key and the value input parameters are used. Again, if the lambda functions look weird then you can consult the course referenced above.

View the list of posts on the Task Parallel Library here.

Advertisement

About Andras Nemes
I'm a .NET/Java developer living and working in Stockholm, Sweden.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

Elliot Balynn's Blog

A directory of wonderful thoughts

Software Engineering

Web development

Disparate Opinions

Various tidbits

chsakell's Blog

WEB APPLICATION DEVELOPMENT TUTORIALS WITH OPEN-SOURCE PROJECTS

Once Upon a Camayoc

Bite-size insight on Cyber Security for the not too technical.

%d bloggers like this: