PROWAREtech

articles » current » dot-net » concurrentdictionary-explained

.NET: ConcurrentDictionary - What is It and How to Use it?

What a ConcurrentDictionary is plus example usage in C#.

See also ConcurrentBag class.

A ConcurrentDictionary in C# is a thread-safe collection that stores key-value pairs, designed for scenarios where multiple threads need to simultaneously add, remove, or modify items. Unlike a regular Dictionary, it provides atomic operations that are safe for concurrent access without explicit locking, making it ideal for producer-consumer patterns or caching scenarios where one needs to ensure data consistency across threads.

A ConcurrentBag, on the other hand, is an unordered collection of objects that allows duplicate values and is optimized for scenarios where the same thread is both adding and removing items. While both collections are thread-safe, ConcurrentBag doesn't maintain key-value relationships and excels in situations where one doesn't need to track specific items but rather just need a thread-safe container for objects, like a pool of resources.

The main difference lies in their use cases: ConcurrentDictionary is best when one needs to maintain unique keys and look up specific values quickly, while ConcurrentBag is more suitable for scenarios where one's just collecting items and don't care about their order or uniqueness. ConcurrentDictionary provides O(1) lookup performance, while ConcurrentBag is optimized for scenarios where the same thread that added an item is likely to remove it.

Example Code


using System;
using System.Collections.Concurrent;
using System.Threading.Tasks;

class Program
{
	static void Main()
	{
		// Create a ConcurrentDictionary
		ConcurrentDictionary<int, string> dict = new ConcurrentDictionary<int, string>();

		// Add or update items in the ConcurrentDictionary in parallel
		Parallel.For(0, 10, i =>
		{
			dict.AddOrUpdate(i, $"Value {i}", (key, oldValue) => $"Value {i}");
			Console.WriteLine($"Added or updated key {i}");
		});

		// Read items from the ConcurrentDictionary
		Parallel.For(0, 10, i =>
		{
			string result;
			if (dict.TryGetValue(i, out result))
			{
				Console.WriteLine($"Key {i} has value {result}");
			}
		});

		// Display contents of the ConcurrentDictionary
		foreach (var kvp in dict)
		{
			Console.WriteLine($"Key: {kvp.Key}, Value: {kvp.Value}");
		}
	}
}

// Creating a cache for expensive operations
using System.Collections.Concurrent;

var cache = new ConcurrentDictionary<string, ExpensiveResult>();

// GetOrAdd - will only compute the value if the key doesn't exist
var result = cache.GetOrAdd("key1", key => {
	// This expensive operation only runs if key1 doesn't exist
	return PerformExpensiveOperation(key);
});

// Thread-safe updates using AddOrUpdate
var userVisits = new ConcurrentDictionary<string, int>();
userVisits.AddOrUpdate(
	"user123",
	// Add value if key doesn't exist
	key => 1,
	// Update value if key exists
	(key, oldValue) => oldValue + 1
);

// Safe removal with TryRemove
if (cache.TryRemove("key1", out var removedValue))
{
	Console.WriteLine($"Successfully removed: {removedValue}");
}

// Atomic updates using GetOrAdd with a factory
var counter = new ConcurrentDictionary<string, int>();
Parallel.ForEach(items, item => {
	counter.AddOrUpdate(
		item.Category,
		// Initial value if key doesn't exist
		category => 1,
		// Update function if key exists
		(category, existingCount) => existingCount + 1
	);
});

// Thread-safe initialization of complex objects
var connections = new ConcurrentDictionary<string, DatabaseConnection>();
var connection = connections.GetOrAdd("db1", serverName => {
	return new DatabaseConnection(serverName, timeout: 30);
});

PROWAREtech

Hello there! How can I help you today?
Ask any question

PROWAREtech

This site uses cookies. Cookies are simple text files stored on the user's computer. They are used for adding features and security to this site. Read the privacy policy.
CLOSE