Threading is a popular component for building powerful and high-performance applications. But like that old saying, “with great power, comes great responsibility”, there is a wide array of potential issues that can arise from having multiple threads accessing values.
One of the most common issues that we as developers need to worry about when creating multithreaded applications is thread safety.
Thread safety is a mechanism that protects from an application from being accessed or modified by multiple threads at the same time. This prevents the deadlocks and data corruption.
In this tutorial, we will learn how to use the “ConcurrentDictionary” class in C# to which provides a thread-safe collection of key-value pairs to use in multi-threaded application.
C# ConcurrentDictionary
The “ConcurrentDictionary” class is part of the “System.Collections.Concurrent” namespace. It provides a thread-safe collection of key-value pairs.
This ensures that multiple threads can read and write the data to the collection without encountering into issues such as deadlocks on the entire collection.
Syntax:
The TKey is the type of the keys in the dictionary and the TValue is the type of the values in the dictionary.
Concurrent Dictionary
To create a concurrent dictionary, we can basically create an instance of the “ConcurrentDictionary” class and provide the key and value types that we wish to store in the dictionary.
Take the following code for example:
namespace Concurrency
{
internal class Program
{
static void Main(string[] args)
{
ConcurrentDictionary<int, string> dict = new ConcurrentDictionary<int, string>();
}
}
}
This creates a concurrent dictionary that holds the integer keys and string values.
Add New Items
To add new items to a concurrent dictionary, we can use the TryAdd() method. The method returns a Boolean which indicates whether the operation is successful or not.
namespace Concurrency
{
internal class Program
{
static void Main(string[] args)
{
ConcurrentDictionary<int, string> dict = new ConcurrentDictionary<int, string>();
bool status = dict.TryAdd(3306, "MySQL");
if (status)
{
Console.WriteLine("success");
}
else
{
Console.WriteLine("Fail.");
}
}
}
}
In the given example, we save the status of the addition operation to a variable and use it to test whether it was successful or not.
Update the Items
Similarly, we can use the TryUpdate() method to update the existing items on a concurrent dictionary. An example is as follows:
The method accepts the key that you wish to update, the new value, and the old value as parameters. Like the TryAdd() method, this also returns a Boolean value.
Fetch the Items
As you can guess, we can use the TryGetValue() method to retrieve the value of a given key in the concurrent dictionary.
namespace Concurrency
{
internal class Program
{
static void Main(string[] args)
{
ConcurrentDictionary<int, string> dict = new ConcurrentDictionary<int, string>();
dict.TryAdd(3306, "MySQL 8");
string value;
bool found = dict.TryGetValue(3306, out value);
if (found)
{
Console.WriteLine(value);
} else {
Console.WriteLine("null");
};
}
}
}
This should fetch the value that is associated with that and store it in a variable.
Concurrent Methods
The class also provides us with methods such as AddOrUpdate to handle the concurrent operations as follows:
In this case, the method either updates the value if it exists. Otherwise, it adds it to the collection.
Conclusion
In this tutorial, we explored how to create and work with thread-safe collection of key-value pairs using the “ConcurrentDictionary” class. Reference the docs for more details.