Home
C#
ConcurrentDictionary Example
Updated Sep 2, 2025
Dot Net Perls

ConcurrentDictionary

The ConcurrentDictionary type from System.Collections.Concurrent allows multiple threads to access a dictionary instance—each thread can call TryAdd().

With ConcurrentDictionary, you get a thread-safe, hash-based lookup table. This type resides in the System.Collections.Concurrent namespace.

Example

This program uses ConcurrentDictionary—it adds keys and values. With ConcurrentDictionary, we use the TryAdd() method. We use System.Threading types to run two threads.

And We call Join() on the two threads and then print the average of all the Values from the ConcurrentDictionary.
using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Threading;

class Program
{
    static ConcurrentDictionary<string, int> _concurrent = new ConcurrentDictionary<string, int>();

    static void Main()
    {
        Thread thread1 = new Thread(new ThreadStart(A));
        Thread thread2 = new Thread(new ThreadStart(A));
        thread1.Start();
        thread2.Start();
        thread1.Join();
        thread2.Join();
        Console.WriteLine("Average: {0}", _concurrent.Values.Average());
    }

    static void A()
    {
        for (int i = 0; i < 1000; i++)
        {
            _concurrent.TryAdd(i.ToString(), i);
        }
    }
}
Average: 499.5

TryUpdate

Here we test TryUpdate. The first call to TryUpdate doesn't do anything, because the value of "cat" is not equal to 4.

Info The second call, though, does change the value from 1 to 100 because its value was set to 1.
using System;
using System.Collections.Concurrent;

class Program
{
    static void Main()
    {
        // New instance.
        var con = new ConcurrentDictionary<string, int>();
        con.TryAdd("cat", 1);
        con.TryAdd("dog", 2);

        // Try to update if value is 4 (this fails).
        con.TryUpdate("cat", 200, 4);

        // Try to update if value is 1 (this works).
        con.TryUpdate("cat", 100, 1);

        // Write new value.
        Console.WriteLine(con["cat"]);
    }
}
100

AddOrUpdate, GetOrAdd

These methods resolve problems with the nature of time in concurrent systems. They ensure the collection will not become corrupted or invalid.

Note Conceptually, the AddOrUpdate method will always result in a value change in the collection.
Note 2 The GetOrAdd method is the same as AddOrUpdate except it will not change the existing value: it will only return it.
using System;
using System.Collections.Concurrent;

class Program
{
    static void Main()
    {
        // New instance.
        var con = new ConcurrentDictionary<string, int>();
        con.TryAdd("cat", 1);
        con.TryAdd("dog", 2);

        // Add dog with value of 5 if it does NOT exist.
        // ... Otherwise, add one to its value.
        con.AddOrUpdate("dog", 5, (k, v) => v + 1);

        // Display dog value.
        Console.WriteLine(con["dog"]);

        // Get mouse or add it with value of 4.
        int mouse = con.GetOrAdd("mouse", 4);
        Console.WriteLine(mouse);

        // Get mouse or add it with value of 660.
        mouse = con.GetOrAdd("mouse", 660);
        Console.WriteLine(mouse);
    }
}
3 4 4

Performance

How fast is ConcurrentDictionary? I tested its lookup performance—in the simple benchmark, 2000 string keys are assigned to ints. Then the key "100" is looked up and timed.

Version 1 We use the ConcurrentDictionary and test for keys, all of which exist in the collection.
Version 2 Here we use the Dictionary collection from System.Collections.Generic, and perform the same tests.
Result In 2025 on Windows, the ConcurrentDictionary has good performance—it is faster in this small benchmark than Dictionary.
#define VERSION1

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;

class Program
{
    public static void Main()
    {
        const int _max = 100000000;

        // ConcurrentDictionary.
        var c = new ConcurrentDictionary<string, int>();
        for (int i = 0; i < 2000; i++)
        {
            c[i.ToString()] = i;
        }

        // Dictionary.
        var d = new Dictionary<string, int>();
        for (int i = 0; i < 2000; i++)
        {
            d[i.ToString()] = i;
        }

        var s1 = Stopwatch.StartNew();
        for (int i = 0; i < _max; i++)
        {
#if VERSION1
            int v;
            if (c.TryGetValue("100", out v))
            {
                if (v != 100)
                {
                    throw new Exception();
                }
            }
#endif
#if VERSION2
            int v2;
            if (d.TryGetValue("100", out v2))
            {
                if (v2 != 100)
                {
                    throw new Exception();
                }
            }
#endif
        }
        s1.Stop();
        Console.WriteLine(((double)(s1.Elapsed.TotalMilliseconds * 1000000) / _max).ToString("0.00 ns"));
    }
}
11.52 ns ConcurrentDictionary 14.45 ns Dictionary

ToArray

The ToArray method on the ConcurrentDictionary type yields an array of KeyValuePair structs. We can use this like any other array.

IsEmpty

This property is equivalent to the expression Count == 0. The collection can never contain a negative number of elements, and is only empty when there are zero elements.

We looked at the ConcurrentDictionary type and its intended usage. The ConcurrentDictionary can enhance lookup performance in complex, time-critical software.

Dot Net Perls is a collection of pages with code examples, which are updated to stay current. Programming is an art, and it can be learned from examples.
Donate to this site to help offset the costs of running the server. Sites like this will cease to exist if there is no financial support for them.
Sam Allen is passionate about computer languages, and he maintains 100% of the material available on this website. He hopes it makes the world a nicer place.
This page was last updated on Sep 2, 2025 (new example).
Home
Changes
© 2007-2025 Sam Allen