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

Kel*_*lly 21 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)
Run Code Online (Sandbox Code Playgroud)

以下调用发生异常:

genericResources = (ResourceDictionary)Application.LoadComponent(new Uri("/Themes/Generic.xaml", UriKind.Relative));
Run Code Online (Sandbox Code Playgroud)

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

Ray*_*rns 27

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

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

  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();
Run Code Online (Sandbox Code Playgroud)

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

 lock(App.MyLoadComponentLock)
   genericDictionary = (ResourceDictionary)Application.LoadComponent(...
Run Code Online (Sandbox Code Playgroud)

  • 多好.似乎没有在.NET 4.5中修复.在https://connect.microsoft.com/VisualStudio/feedback/details/758055/application-loadcomponent-is-not-thread-safe上创建了连接问题. (2认同)