Working with global Dictionary inside threads

Suppose that I have a Dictionary<string, string>. The dictionary is declared as public static in my console program.

If I’m working with threads and I want to do foreach on this Dictionary from one thread but at the same time another thread want to add item to the dictionary. This would cause a bug here because we can’t modify our Dictionary while we are running on it with a foreach loop in another thread.

To bypass this problem I created a lock statement on the same static object on each operation on the dictionary.

Is this the best way to bypass this problem? My Dictionary can be very big and I can have many threads that want to foreach on it. As it is currently, things can be very slow.

Here is Solutions:

We have many solutions to this problem, But we recommend you to use the first solution because it is tested & true solution that will 100% work for you.

Solution 1

Try using a ConcurrentDictionary<TKey, TValue>, which is designed for this kind of scenario.

There’s a nice tutorial here on how to use it.

Solution 2

The big question is: Do you need the foreach to be a snapshot?

If the answer is “no”, then use a ConcurrentDictionary and you will probably be fine. (The one remaining question is whether the nature of your inserts and reads hit the striped locks in a bad way, but if that was the case you’d be finding normal reads and writes to the dictionary even worse).

However, because it’s GetEnumerator doesn’t provide a snapshot, it will not be enumerating the same start at the beginning as it is at the end. It could miss items, or duplicate items. The question is whether that’s a disaster to you or not.

If it would be a disaster if you had duplicates, but not otherwise, then you can filter out duplicates with Distinct() (whether keyed on the keys or both the key and value, as required).

If you really need it to be a hard snapshot, then take the following approach.

Have a ConcurrentDictionary (dict) and a ReaderWriterLockSlim (rwls). On both reads and writes obtain a reader lock (yes even though you’re writing):

public static void AddToDict(string key, string value)
{
  rwls.EnterReadLock();
  try
  {
    dict[key] = value;
  }
  finally
  {
    rwls.ExitReadLock();
  }
}
public static bool ReadFromDict(string key, out string value)
{
  rwls.EnterReadLock();
  try
  {
    return dict.TryGetValue(key, out value);
  }
  finally
  {
    rwls.ExitReadLock();
  }
}

Now, when we want to enumerate the dictionary, we acquire the write lock (even though we’re reading):

public IEnumerable<KeyValuePair<string, string>> EnumerateDict()
{
  rwls.EnterWriteLock();
  try
  {
    return dict.ToList();
  }
  finally
  {
    rwls.ExitWriteLock();
  }
}

This way we obtain the shared lock for reading and writing, because ConcurrentDictionary deals with the conflicts involved in that for us. We obtain the exclusive lock for enumerating, but just for long enough to obtain a snapshot of the dictionary in a list, which is then used only in that thread and not shared with any other.

Solution 3

With .NET 4 you get a fancy new ConcurrentDictionary. I think there are some .NET 3.5-based implementations floating around.

Solution 4

Yes, you will have a problem updating the global dictionary while an enumeration is running in another thread.

Solutions:

  1. Require all users of the dictionary to acquire a mutex lock before accessing the object, and release the lock afterwards.
  2. Use .NET 4.0’s ConcurrentDictionary class.

Note: Use and implement solution 1 because this method fully tested our system.
Thank you 🙂

All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply