【问题标题】:Threading errors with Application.LoadComponent (key already exists)Application.LoadComponent 的线程错误(键已存在)
【发布时间】:2011-01-28 15:51:40
【问题描述】:

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 点时,我每次都会收到错误消息。难道我做错了什么?我该怎么做才能解决这个问题?

【问题讨论】:

标签: c# wpf multithreading thread-safety


【解决方案1】:

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

问题在于,每当 Application.LoadComponent 从“包”加载“部件”时:

  1. 检查包的内部缓存以查看部件是否已加载,如果找到则返回
  2. 从文件中加载部件
  3. 将其添加到内部缓存
  4. 退货

您有两个线程调用Application.LoadComponent 来同时加载相同的部分。 MSDN 文档说这没问题,但发生的情况是:

  1. 线程 #1 检查缓存并开始从文件加载
  2. 线程 #2 检查缓存并开始从文件加载
  3. 线程 #1 完成从文件加载并添加到缓存中
  4. 线程 #2 完成从文件加载并尝试添加到缓存中,导致重复键异常

该错误的解决方法是将所有对 Application.LoadComponent 的调用包装在 lock() 中。

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

 public static object MyLoadComponentLock = new Object();

那么您的 LoadComponent 调用如下所示:

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

【讨论】:

  • 没有。我一直假设微软已经在监控 StackOverflow 并在内部生成错误报告。如果没有,他们就忽略了一个非常有用的 QA 资源。
  • 感谢您的回答。我们遇到了与原始问题发布者几乎完全相同的问题,在 MSDN 中找到此错误描述无疑为我们节省了数小时的调查时间。
  • 我在使用不同数据的多个新线程上加载同一个窗口时收到此错误。我将锁添加到所有新窗口调用以解决此问题。
  • 再次,我发现了这个错误。我们正在运行多个调度程序,当用户双击打开一个新项目时,这会咬我们,每个项目都在一个新线程和调度程序上,并且会发生此错误...
  • 真好。 .NET 4.5 中似乎没有修复。在connect.microsoft.com/VisualStudio/feedback/details/758055/… 创建了一个连接问题。
【解决方案2】:

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

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

(from the msdn documentation)

更新:
尝试在拨打LoadComponent 时进行同步,看看问题是否仍然存在。

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

公共静态(Shared in Visual Basic) 这种类型的成员是线程 安全的。此外,FindResource 和 TryFindResource 方法和 属性和资源属性 是线程安全的。

它确实说线程安全,但如果它复制相同的键,那么它们一定是指其他类型的线程安全。

【讨论】:

  • 向排序列表添加键的不是我的代码。我更新了问题以表明我只是在调用 Application.LoadComponent()。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-04-09
  • 2019-07-25
  • 2019-09-04
  • 2020-10-13
相关资源
最近更新 更多