Search code examples
c#concurrencyconcurrentdictionary

Using the Concurrent Dictionary - Thread Safe Collection Modification


Recently I was running into the following exception when using a generic dictionary

An InvalidOperationException has occurred. A collection was modified

I realized that this error was primarily because of thread safety issues on the static dictionary I was using.

A little background: I currently have an application which has 3 different methods that are related to this issue.

  1. Method A iterates through the dictionary using foreach and returns a value.
  2. Method B adds data to the dictionary.
  3. Method C changes the value of the key in the dictionary.

Sometimes while iterating through the dictionary, data is also being added, which is the cause of this issue. I keep getting this exception in the foreach part of my code where I iterate over the contents of the dictionary. In order to resolve this issue, I replaced the generic dictionary with the ConcurrentDictionary and here are the details of what I did.

Aim : My main objective is to completely remove the exception

For method B (which adds a new key to the dictionary) I replaced .Add with TryAdd

For method C (which updates the value of the dictionary) I did not make any changes. A rough sketch of the code is as follows :

  static public int ChangeContent(int para)
  {
      foreach (KeyValuePair<string, CustObject> pair in static_container)
      {
             if (pair.Value.propA != para ) //Pending cancel 
             {
                pair.Value.data_id = prim_id;    //I am updating the content
                return 0;

             }
      }
     return -2;
  }

For method A - I am simply iterating over the dictionary and this is where the running code stops (in debug mode) and Visual Studio informs me that this is where the error occured.The code I am using is similar to the following

    static public CustObject RetrieveOrderDetails(int para)
    {
            foreach (KeyValuePair<string, CustObject> pair in static_container)
            {                   
                if (pair.Value.cust_id.Equals(symbol))
                {
                    if (pair.Value.OrderStatus != para) 
                    {
                       return pair.Value; //Found
                    }
                }
            }
            return null; //Not found
    }

Are these changes going to resolve the exception that I am getting.

Edit:

It states on this page that the method GetEnumerator allows you to traverse through the elements in parallel with writes (although it may be outdated). Isnt that the same as using foreach ?


Solution

  • Before doing foreach() try out copying container to a new instance

    var unboundContainer = static_container.ToList();
    foreach (KeyValuePair<string, CustObject> pair in unboundContainer)
    

    Also I think updating Value property is not right from thread safety perspectives, refactor your code to use TryUpdate() instead.