Is it more efficient to lock or try-catch dictionary in a thread (.NET) Is it more efficient to lock or try-catch dictionary in a thread (.NET) multithreading multithreading

Is it more efficient to lock or try-catch dictionary in a thread (.NET)


I think you should test it in your own environment. Basically:

  • Lock is cheap
  • Try without getting an exception is cheap, maybe even cheaper then lock
  • Try and getting exception is very expensive

So now the question is, how often you expect to have cache-miss, and therefore get an exception thrown. I would go for lock() as it's execution time is not dependent on whether you will or not get cache-hit, which means it's more predictable and measurable, while still - very cheap. I don't think that hundreds hits per second would be any problem.

Simple tests I've made indicate, that getting cache-miss with try/catch is very, very expensive.

Edit:

Simple test shows that:

  • try-no throw costs about 2ms for 100k retrieves
  • lock costs about 6ms
  • try-throw costs about 4seconds

Which means, got for lock(), because it's more efficient then try/catch if you're getting more then 1 cache miss per few thousands tries, and it's much more stable, being not depended on luck.


You can go ahead and write off the try-catch option. I do not know if it is slower or not, but I do know that it will not always yield correct, consistent, and predictable results if there is another thread updating the Dictionary. The problem is that at some point the writer will have the Dictionary in a half-baked state and there is no telling what the readers will see. This just will not work.

Option 1: If .NET 4.0 is available to you then I would use ConcurrentDictionary.

Option 2: If you are using .NET 3.5 then you can download the Reactive Extensions backport. ConcurrentDictionary is included in System.Threading.dll.

Option 3: Another idea is to keep two separate copies of the Dictionary. One would only be used for reading while the other would serve as the official copy that accepts updates. Anytime you update the "official" Dictionary you would clone it and overwrite the reference of the copy.

public class Example{  // This is the official version which can accept updates.  private readonly Dictionary<int, CustomObject> official = new Dictionary<int, CustomObject>();  // This is a readonly copy. This must be marked as volatile for this to work correctly.  private volatile Dictionary<int, CustomObject> copy = new Dictionary<int, CustomObject>();  public class Example()  {  }  public void Set(int id, CustomObject value)  {    lock (official)    {      // Update the official dictionary.      official[id] = value;      // Now create a clone of the official dictionary.      var clone = new Dictionary<int, CustomObject>();      foreach (var kvp in official)      {        clone.Add(kvp.Key, kvp.Value);      }      // Swap out the reference.       copy = clone;    }  }  public CustomObject Get(int id)  {       // No lock is required here.    CustomObject value = null;    if (copy.TryGetValue(id, out value))    {      return value;    }    return null;  }}

This option does not work well if there are a lot of items in the Dictionary or if updates to the official copy happen frequently. But, it is a trick I do use from time to time.

Option 4: An equally reasonable approach would be to stick with the plain old lock.


I would go with lock/interlocked in your scenario. You could get currupted/invalid data if you don't lock, and try to write something to the dictionary, somewhere else.

If you are too mutch concerned about performance, you can use Interlocked class... there are lots of techniques on how to use it, to achieve locking with more performance than lock.

An implementation would be possible by using Interlocked.CompareExchange and using a control variable.

Interlocked sample:

I found this sample in Microsoft site (just copied so that you can see it here):

Source: http://msdn.microsoft.com/en-us/library/system.threading.interlocked(v=VS.100).aspx#Y1291

        if(0 == Interlocked.Exchange(ref usingResource, 1))        {            Console.WriteLine("{0} acquired the lock", Thread.CurrentThread.Name);            //Code to access a resource that is not thread safe would go here.            //Simulate some work            Thread.Sleep(500);            Console.WriteLine("{0} exiting lock", Thread.CurrentThread.Name);            //Release the lock            Interlocked.Exchange(ref usingResource, 0);            return true;        }        else        {            Console.WriteLine("   {0} was denied the lock", Thread.CurrentThread.Name);            return false;        }

Note that, in this sample, almost everything inside the if block is locked!

The variable usingResource is a field of the class, being passed by reference to the method... it is the control variable I mentioned earlier.