21

In my code I have a static dictionary object

private static IDictionary< ConnKey, DbConnection > ConnectionList = new Dictionary< ConnKey, DbConnection >( );

which is throwing this error

System.IndexOutOfRangeException: Index was outside the bounds of the array.
  at System.Collections.Generic.Dictionary`2.Insert(TKey key, TValue value, Boolean add)
  at System.Collections.Generic.Dictionary`2.Add(TKey key, TValue value)

I searched and found that this occurs because multiple threads try to access dictionary, but I do have lock on dictionary

lock( ConnectionList ) {
   ConnectionList.Add( key, res );
}

Then I searched more and found that lock on dictionary doesn't prevent all the operations on it so I should be using lock on SyncRoot object of it like this to achieve what I want

lock( ((IDictionary)ConnectionList).SyncRoot) {

But then I searched that using SyncRoot is not a good practice

On further search I found there is a ConcurrentDictionary for this purpose

  1. So can anybody please suggest me which is the best way to lock the dictionary
  2. If I use ConcurrentDictionary do I still need to use lock on it or will it handle everything by itself.
  3. If I have to use lock on ConcurrentDictionary, I have to use lock on it directly or again I have to lock the SyncRoot object for it

Thanks in advance!

Pawan Nogariya
  • 8,330
  • 12
  • 52
  • 105

3 Answers3

22

With Dictionary<,> you have to lock both reading and writing. So both

lock( ConnectionList ) {
   ConnectionList.Add( key, res );
}

and

lock( ConnectionList ) {
   res = ConnectionList[ key ];
}

and

lock( ConnectionList ) {
   int cnt = ConnectionList.Count;
}

and

lock( ConnectionList ) {
   ConnectionList.Clear();
}

and

lock( ConnectionList ) {
   foreach ( var kv in ConnectionList ) {
      // Do things
   }
}

and so on :-)

With ConcurrentDictionary<,> you don't need any locking, but note that the syntax is a little different than the one of the Dictionary<,>

xanatos
  • 109,618
  • 12
  • 197
  • 280
  • Thanks! So you mean everytime I access the dictionary I should lock it first and If I do that it will become thread safe, right? – Pawan Nogariya Apr 10 '15 at 09:27
  • 1
    @PawanNogariya Yep. **Any** and **all** access must be in a `lock`. There are no methods/properties that are connected to accessing the data in the `Dictionary<,>` that are guaranteed to be thread safe (with *that are connected to accessing* I mean that `.GetType()` and similar methods are still thread safe, but not very useful for using a `Dictionary<,>` :-) ) – xanatos Apr 10 '15 at 09:30
  • Ok, but wouldn't it make code ugly if I have to access the dictionary frequently, in that case will you suggest `ConcurrentDictionary` over this method and I never have to lock it, right? And its just the syntax difference, right? – Pawan Nogariya Apr 10 '15 at 09:34
  • 1
    @PawanNogariya It all depends on how you want to use the `Dictionary<>`. If you will have concurrent reads and writes then `ConcurrentDictionary<>` is better. But if, for example, you have only an initial multithreaded load phase and when it finishes you only read, then using the `lock` is better. Note that if there are only readers, then `Dictionary<>` is thread safe. See Thread Safety in https://msdn.microsoft.com/it-it/library/xfhwa508(v=vs.110).aspx: *A Dictionary can support multiple readers concurrently, as long as the collection is not modified.* – xanatos Apr 10 '15 at 09:37
  • @JayShah The exact explanation is complex, the easiest way to imagine it is that to make a read to a dictionary, there are multiple steps done inside the `Dictionary<,>` class. The fact that there are multiple steps means that it could be not-thread-safe – xanatos Jan 10 '18 at 22:22
3
  1. So can anybody please suggest me which is the best way to lock the dictionary

You can use it's SyncRoot or create a private object that you lock when accessing the dictionary object, e.g.

private static object _sybcRoot = new object();

public static void Add( string key, string res)
    lock( _sybcRoot ) {
       ConnectionList.Add( key, res );
    }
}

You have to use the same lock object to guard the access to the same resource. Otherwise threads may "think" the resource is free, whereas in reality it is used by the other thread which just happen to lock it on the other object's sync root.

  1. If I use ConcurrentDictionary do I still need to use lock on it or will it handle everything by itself.

No, there is no need for locking when using any Concurrent* collection. It is thread-safe by design, but this syntax is slightly different. Concurrent* collections use lockless approach, which is better in a situations when you don't have many threads competing for access (optimistic concurrency)

  1. If I have to use lock on ConcurrentDictionary, I have to use lock on it directly or again I have to lock the SyncRoot object for it
James Hirschorn
  • 7,032
  • 5
  • 45
  • 53
oleksii
  • 35,458
  • 16
  • 93
  • 163
3

So can anybody please suggest me which is the best way to lock the dictionary?

if you want to continue using the classic Dictionary<,> AFAK you have to look to ICollection interface implemented by Dictionary and use the property ICollection.SyncRoot which by definition

MSDN Gets an object that can be used to synchronize access to the ICollection. So to achieve this you can do something like this

If I use ConcurrentDictionary do I still need to use lock on it or will it handle everything by itself.

From MSDN
ConcurrentDictionary is designed for multithreaded scenarios. You do not have to use locks in your code to add or remove items from the collection. However, it is always possible for one thread to retrieve a value, and another thread to immediately update the collection by giving the same key a new value.

If I have to use lock on ConcurrentDictionary, I have to use lock on it directly or again I have to lock the SyncRoot object for it

Yes you have to use lock on SyncRoot if you want to do Atomic methods execution when you use GetOrAdd or AddOrUpdate methods

BRAHIM Kamel
  • 13,492
  • 1
  • 36
  • 47