Application.LoadComponent的线程错误(密钥已存在)

时间:2010-03-17 16:03:03

标签: c# wpf multithreading thread-safety

MSDN说System.Windows.Application的公共静态成员是线程安全的。但是当我尝试使用多个线程运行我的应用程序时,我得到以下异常:

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)

以下调用发生异常:

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

应用程序在单个线程上运行正常,甚至在两个或三个线程上运行。当我从5点起床后,每次都会收到错误。难道我做错了什么?我该怎么做才能解决这个问题?

2 个答案:

答案 0 :(得分:27)

你没有做错事。 MSDN错了。 Application.LoadComponent实际上不是线程安全的。在我看来,这是WPF中的一个错误。

问题是每当Application.LoadComponent从“Package”加载“Part”时:

  1. 检查其内部缓存以查看该部件是否已加载&如果找到则返回
  2. 从文件中加载部分
  3. 将其添加到内部缓存
  4. 返回
  5. 您有两个线程调用Application.LoadComponent同时加载相同的部分。 MSDN文档说这没关系,但发生的事情是:

    1. 线程#1检查缓存并从文件开始加载
    2. 线程#2检查缓存并从文件开始加载
    3. 线程#1完成从文件加载并添加到缓存
    4. 线程#2完成从文件加载并尝试添加到缓存,导致重复的键异常
    5. 该错误的解决方法是将所有对Application.LoadComponent的调用包装在lock()中。

      您的锁定对象可以在App.cs或其他地方(您的选择)中创建:

       public static object MyLoadComponentLock = new Object();
      

      然后你的LoadComponent调用如下所示:

       lock(App.MyLoadComponentLock)
         genericDictionary = (ResourceDictionary)Application.LoadComponent(...
      

答案 1 :(得分:0)

看起来地图中已添加了具有相同键的项目。这不是一个线程问题,这是你所拥有的程序的一个问题。一个线程向地图添加了键/值对,第二个线程正在尝试添加具有相同键的值,您是如何在两个单独的线程上获得相同的键的?你是如何生成密钥的?

  

SortedList对象的元素   按键排序   根据特定的IComparer   实现时指定的   SortedList是根据或创建的   IComparable实现   由钥匙本身提供。在   无论是哪种情况,一个SortedList都没有   允许重复密钥。

(from the msdn documentation)

<强>更新
在您拨打LoadComponent时尝试同步并查看问题是否仍然存在。

当他们说出以下内容时,我根本不知道他们的意思:

  

公共静态(在Visual中共享)   基本)这种类型的成员是线程   安全。另外,还有FindResource   和TryFindResource方法和   属性和资源属性   是线程安全的。

确实说线程安全,但如果它复制相同的密钥,那么它们必须引用其他类型的线程安全。