System.ArgumentException: An item with the same key has already been added

Intermitently my code was getting this Exception: "System.ArgumentException: An item with the same key has already been added."  The code looked like this:

if (!_roleTypeNameDict.ContainsKey(roleTypeName))
_roleTypeNameDict.Add(roleTypeName, ...)

Since I have done a lot of C++ programming right away I realized that I had a threading issue, however the question was why? It just so happens this was a static method, calling a static property, which means that more then one thread might be accessing _roleTypeNameDict at a time:

private static Dictionary<String, RoleTypes> _roleTypeNameDict
   = new Dictionary<string, RoleTypes>();

public static RoleTypes FindCachedRoleTypesFromRoleTypeName(String roleTypeName)
{

if (!_roleTypeNameDict.ContainsKey(roleTypeName))
       _roleTypeNameDict.Add(roleTypeName, ...);
...
}

To solve the issue you need to create a lock to protect the check to add from the add like this:

private static Dictionary<String, RoleTypes> _roleTypeNameDict
   = new Dictionary<string, RoleTypes>();
private
static object _roleTypeNameDictLock = new object(); public static RoleTypes FindCachedRoleTypesFromRoleTypeName(String roleTypeName) {    lock (_roleTypeNameDictLock) {    if (!_roleTypeNameDict.ContainsKey(roleTypeName))       _roleTypeNameDict.Add(roleTypeName, ...); }
   ...
}

However, you can make the code slightly faster if you assume that the ContainKey is less expense then the lock from a performance stand point:

private static Dictionary<String, RoleTypes> _roleTypeNameDict
   = new Dictionary<string, RoleTypes>();
private static object _roleTypeNameDictLock = new object();

public static RoleTypes FindCachedRoleTypesFromRoleTypeName(String roleTypeName)
{
   if (!_roleTypeNameDict.ContainsKey(roleTypeName))
   {
      lock (_roleTypeNameDictLock)
      {
         if (!_roleTypeNameDict.ContainsKey(roleTypeName))
            _roleTypeNameDict.Add(roleTypeName, ...);
      }
   }
   ...
}

What we are doing here is checking to see if we need to add, then locking, then checking again, then adding.  This prevents us from locking if we don't need to add-- the majoirty of the cases, however it also prevents two threads from trying to add at the same time.

I was just thinking the other day that threading, caching, and memory management skills where still needed in .NET even though the Microsoft PR machines is making C# out to be much easier then C++.  At the same time VBScript programmers in classic ASP don't have to worry about threading issues.

{6230289B-5BEE-409e-932A-2F01FA407A92}

 
 

Comments

Popular posts from this blog

Simple WP7 Mango App for Background Tasks, Toast, and Tiles: Code Explanation

Yet once more into the breech (of altered programming logic)

Error : /ScriptResource.axd : Invalid viewstate.