Is it more efficient to block or try to use a dictionary in a stream (.NET)

I have a generic dictionary that I use as a cache in a threaded .NET project (C #). I will do many readings in the dictionary (potentially hundreds or more per second at peak times).

I have a simple Get (int id) method that should return a CustomObject if it is in the cache, or null otherwise.

My question is: Is it faster / more efficient to block a dictionary or just use a try / catch block?

Assuming the dictionary is stored in a variable named "dic".

Lock Example:

public CustomObject Get(int id)
{
   lock(dic)
   {
      if (dic.ContainsKey(id))
         return dic[id];
   }
   return null;
}

Try / catch example:

public CustomObject Get(int id)
{
   try
   {
      return dic[id];
   }
   catch
   {
      return null;
   }
}
+5
source share
6 answers

, . :

  • , , , , ,

, , , -, , , . lock(), , -, , , - . , .

, , , - try/catch .

Edit

, :

  • - 2 100 .
  • 6
  • 4

, lock() , , try/catch, 1 , , .

+8

try-catch. , , , , , , Dictionary. , - Dictionary , , . .

1: .NET 4.0, ConcurrentDictionary.

2: .NET 3.5, Reactive Extensions backport. ConcurrentDictionary System.Threading.dll.

3: , Dictionary. , , . , "" Dictionary, .

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;
  }

}

, Dictionary . , .

4: , lock.

+6

lock/interlocked . , / - , - .

, Interlocked... , , lock.

Interlocked.CompareExchange .

:

Microsoft ( , ):

: 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;
        }

, if !

usingResource , ... , .

+4

, , .

+1

= > !!

, , , . lock() 50 , ! , .

, 6ms/100k, lock(), (), 60ns . , , , .

, , , , , , , .

- , , .

. , -, , , .

, , ConcurrentDictionary TPL, dotNET 3.5 ( TPL nuget) .

0

Improved class example from Brian Gideon's answer. It is a good idea when you read meaningful texts. I made it a universal class, replaced volatile with blocked methods, and restored one possible race condition during initial initialization.

using System.Threading;
using System.Collections.Generic;


public class Example<TKeys, TValues>
{
    private readonly object obj = new object();

    // Read/Write version.
    private readonly Dictionary<TKeys, TValues> official = new Dictionary<TKeys, TValues>();

    // Read/Replace version. 
    //it must be initialized as new empty dictionary as well,
    // they can not share reference or it won't be thread safe
    //initially because Set be inserting data into official and copy
    // in the same time.
    private Dictionary<TKeys, TValues> copy = new Dictionary<TKeys, TValues>();

    public void Set(TKeys id, TValues value)
    {
        lock (obj)
        {
            // Update the official dictionary.
            official[id] = value;

            // Now create a clone of official dictionary
            var clone = new Dictionary<int, CustomObject>(official);

            // interlocked sets atomically latest reference
            Interlocked.Exchange(ref copy, clone);
        }
    }

    public bool TryGetValue(TKeys id, out TValues value)
    {
        // interlocked gets latest reference atomically,
        //also providing access to full TryGetValue makes it
        //safer for struct data types, if dictionary empty,
        //it wont return default value!
        return
        Interlocked
        .CompareExchange(ref copy, null, null)
        .TryGetValue(id, ref value)
        ;
    }

}
0
source

All Articles