views:

220

answers:

2

MSDN says that public static members of System.Windows.Application are thread safe. But when I try to run my app with multiple threads I get the following exception:

ArgumentException: An entry with the same key already exists.

   at System.ThrowHelper.ThrowArgumentException(ExceptionResource resource)
   at System.Collections.Generic.SortedList`2.Add(TKey key, TValue value)
   at System.IO.Packaging.Package.AddIfNoPrefixCollisionDetected(ValidatedPartUri partUri,
        PackagePart part)
   at System.IO.Packaging.Package.GetPartHelper(Uri partUri)
   at System.IO.Packaging.Package.GetPart(Uri partUri)
   at System.Windows.Application.GetResourceOrContentPart(Uri uri)
   at System.Windows.Application.LoadComponent(Uri resourceLocator, Boolean 
bSkipJournaledProperties)
       at System.Windows.Application.LoadComponent(Uri resourceLocator)

The exception occurs on the following call:

genericResources = (ResourceDictionary)Application.LoadComponent(new Uri("/Themes/Generic.xaml", UriKind.Relative));

The application works fine on a single thread and even on two or three. When I get up past 5 then I get the error every time. Am I doing something wrong? What can I do to fix this?

A: 

It looks like an item with the same key has already been added in the map. It's not a threading issue, it's an issue with the program you have. One thread has added a key/value pair to the map and a second thread is attempting to add a value with an identical key, how did you end up having identical keys on two separate threads? How are you generating the keys?

The elements of a SortedList object are sorted by the keys either according to a specific IComparer implementation specified when the SortedList is created or according to the IComparable implementation provided by the keys themselves. In either case, a SortedList does not allow duplicate keys.

(from the msdn documentation)

Update:
Try synchronizing when you're calling LoadComponent and see if the issue still persists.

I simply don't know what they mean when they say the following:

The public static (Shared in Visual Basic) members of this type are thread safe. In addition, the FindResource and TryFindResource methods and the Properties and Resources properties are thread safe.

It sure does say thread safe, but if it's duplicating identical keys, then they must be referring to some other type of thread safety.

Lirik
It is not my code that is adding keys to the sorted list. I updated the question to show that I am simply calling Application.LoadComponent().
Kelly
+3  A: 

You are not doing something wrong. MSDN is wrong. Application.LoadComponent is not actually thread safe. This is a bug in WPF, in my opinion.

The problem is that whenever Application.LoadComponent loads a "Part" from a "Package" it:

  1. Checks its internal cache for the package to see if the part is already loaded & returns it if found
  2. Loads the part from the file
  3. Adds it to the internal cache
  4. Returns it

You have two threads calling Application.LoadComponent to load the same part at the same time. The MSDN documentation says this is ok, but what is happening is:

  1. Thread #1 checks the cache and starts loading from the file
  2. Thread #2 checks the cache and starts loading from the file
  3. Thread #1 finishes loading from the file and adds to the cache
  4. Thread #2 finishes loading from the file and tries to add to the cache, resulting in a duplicate key exception

The workaround for the bug is to wrap all calls to Application.LoadComponent inside a lock().

Your lock object can be created thusly in your App.cs or elsewhere (your choice):

 public static object MyLoadComponentLock = new Object();

Then your LoadComponent call looks like this:

 lock(App.MyLoadComponentLock)
   genericDictionary = (ResourceDictionary)Application.LoadComponent(...
Ray Burns
Thats what I suspected, I was hoping I was wrong.
Kelly
+1 Ray, I still can't believe it tho...
Lirik
@Ray did you file a bug on http://connect.microsoft.com ?
JaredPar
No. I have been proceeding on the assumption that Microsoft is already monitoring StackOverflow and generating bug reports internally. If not, they are ignoring a very useful QA resource.
Ray Burns