Подтвердить что ты не робот

Ошибки Threading с Application.LoadComponent(ключ уже существует)

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, я получаю ошибку каждый раз. Я делаю что-то неправильно? Что я могу сделать, чтобы исправить это?

4b9b3361

Ответ 1

Вы не делаете что-то не так. MSDN ошибочна. Application.LoadComponent на самом деле не является потокобезопасным. Это, по-моему, ошибка в WPF.

Проблема в том, что всякий раз, когда Application.LoadComponent загружает "Часть" из "Пакета", она:

  • Проверяет свой внутренний кеш для пакета, чтобы узнать, загружена ли часть и возвращает ли она, если найдена
  • Загрузка части из файла
  • Добавляет его во внутренний кеш
  • Возвращает его

У вас есть два потока, вызывающих Application.LoadComponent для загрузки одной и той же части одновременно. Документация MSDN говорит, что это нормально, но происходит следующее:

  • Тема №1 проверяет кеш и начинает загрузку из файла
  • Тема №2 проверяет кеш и начинает загрузку из файла
  • Тема № 1 завершает загрузку из файла и добавляет в кеш
  • Тема №2 завершает загрузку из файла и пытается добавить в кэш, что приводит к дублированию ключевого исключения.

Обходной путь для ошибки заключается в том, чтобы обернуть все вызовы Application.LoadComponent внутри блокировки().

Ваш объект блокировки может быть создан таким образом в вашем App.cs или в другом месте (на ваш выбор):

 public static object MyLoadComponentLock = new Object();

Затем ваш запрос LoadComponent выглядит следующим образом:

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

Ответ 2

Похоже, что элемент с тем же ключом уже добавлен на карте. Это не проблема с потоками, это проблема с вашей программой. Один поток добавил пару ключ/значение к карте, а второй поток пытается добавить значение с помощью идентичного ключа, как у вас в итоге были идентичные ключи на двух отдельных потоках? Как вы генерируете ключи?

Элементы объекта SortedList сортируются по клавишам в соответствии с конкретным IComparer реализации, если SortedList создается или согласно реализация IComparable предоставляемые самими ключами. В в любом случае a SortedList не позволяют дублировать ключи.

(из документации msdn)

Update:
Попробуйте синхронизировать, когда вы вызываете LoadComponent, и посмотрите, сохраняется ли проблема.

Я просто не знаю, что они имеют в виду, когда говорят следующее:

Публичный статический (Shared in Visual Basic) членами этого типа являются потоки безопасно. Кроме того, FindResource и TryFindResource и Свойства и свойства безопасны по потоку.

Он уверен, что потокобезопасен, но если он дублирует идентичные ключи, тогда они должны ссылаться на некоторые другие типы безопасности потоков.