Multithreaded access to a Dictionary

I’ve search across the web and I get a bit confused about multithreading (lock, Monitor.Enter, volatile etc.) So, instead of asking the solution here, I’ve tried something “homemade” about multithreading management and I would like to have your advices.

Here’s my context :

-I have a static class that contains a static Dictionary<int,string>

-I have a lot of tasks (let’s say 1000) reading in this Dictionary every seconds

-I have one another task, that will update this Dictionary every 10s.

Here’s the code of the cache:

public static class Cache
{
    public static bool locked = false;
    public static Dictionary<int, string> Entries = new Dictionary<int, string>();
    public static Dictionary<int, string> TempEntries = new Dictionary<int, string>();

    // Called by 1000+ Tasks
    public static string GetStringByTaskId(int taskId)
    {
        string result;

        if (locked)
            TempEntries.TryGetValue(taskId, out result);
        else
            Entries.TryGetValue(taskId, out result);

        return result;
    }

    // Called by 1 task
    public static void UpdateEntries(List<int> taskIds)
    {
        TempEntries = new Dictionary<int, string>(Entries);

        locked = true;
        Entries.Clear();

        try
        {
            // Simulates database access
            Thread.Sleep(3000);

            foreach (int taskId in taskIds)
            {
                Entries.Add(taskId, $"task {taskId} : {DateTime.Now}");
            }
        }
        catch (Exception ex)
        {
            Log(ex);
        }
        finally
        {
            locked = false;
        }
    }
}

There’s my questions :

The program runs but I don’t understand why the twice ‘locked’ bool assignments in the UpdateEntries methods doesn’t generate a multithreading exception since it’s read by the other thread “everytime”

Is there a more conventional way to handle this, I feel like it’s a weird way to do it?

Answer

The conventional way to handle this is to use a ConcurrentDictionary. This class is thread-safe and designed for multiple threads to read and write to it. You still need to be aware of potential logic problems (e.g. if two keys must be added at the same time before other threads can see either of them), but it is going to be fine for most operations without additional locking.

Another way to handle this for your specific situation is to use an ordinary dictionary, but treat it as immutable once it is available to reader threads. This will be more efficient as it avoids locks.

public static void UpdateEntries(List<int> taskIds)
{
    //Other threads can't see this dictionary
    var transientDictionary = new Dictionary<int, string>();  

    foreach (int taskId in taskIds)
    {
        transientDictionary.Add(taskId, $"task {taskId} : {DateTime.Now}");
    }

    //Publish the new dictionary so other threads can see it
    TempEntries = transientDictionary; 
}

Once the dictionary is assigned to TempEntries (the only place other threads can access it), it is never modified, so the threading concerns go away.

Leave a Reply

Your email address will not be published. Required fields are marked *