Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

.net Dictionary.Resize() exception - thread safety?


We have a very serious problem that's causing thousands of exceptions per minute. We've got a website that runs a home-grown caching mechanism that holds data in the form of:

protected static IDictionary<int, IList<IInterfaceForData>> m_Data = null;

and when we call Add on this dictionary, we get a very bizarre behavior: "Index was outside the bounds of the array", when the key was 100% not in the dictionary:

m_Data.Add(id, new List<IInterfaceForData>());

We protect this call using a lock like this:

if(Monitor.TryEnter(m_LockObj, 1000))
{
   try
   {
       m_Data.Add(id, new List<IInterfaceForData>());
   }
   catch(Exception ex)
   {                                   
        // log exception
   }
   finally
   {
      Monitor.Exit(m_LockObj);
   }
}

and we get this exception:

   at System.Collections.Generic.Dictionary`2.Resize()     at   System.Collections.Generic.Dictionary`2.Insert(TKey key, TValue value, Boolean add)       at System.Collections.Generic.Dictionary`2.Add(TKey key, TValue value)     

We can't find any explanation for this because the exception is related to Dictionary's thread safety, and we (think we) are thread safe. We use lock() or Monitor.TryEnter on every Add() and Remove() calls, except for m_Data.TryGetValue(...)

Any help would be greatly appreciated.

thanks a lot.

like image 676
Nir Avatar asked Aug 31 '25 10:08

Nir


1 Answers

It seems like at some point the code is not locked and the collection is changed... Have you taken a look at the System.Collections.Concurrent namespace? Specifically the ConcurrentDictionary class? It is thread safe and will probably save you from lots of pains of weird bugs like that or race conditions, etc

It works almost like the regular dictionary , except that for most operations you use a "try" method ie TryGetValue this will try to get the value and return True if the operation was valid and False if not, you can then of course check against this values to continue with your logic

You should check-out this msdn link, its really similar to what you are doing:

Implementing a cache with ConcurrentDictionary

the asker is currently using a non-concurrent dictionary with a ReaderWriterLockSlim and is changing it to a concurrent dictionary.

like image 90
Francisco Noriega Avatar answered Sep 03 '25 18:09

Francisco Noriega