Потоковые ошибки с Application.LoadComponent (ключ уже существует) - PullRequest
21 голосов
/ 17 марта 2010

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 ]

27 голосов
/ 17 марта 2010

Вы не делаете что-то не так. 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(...
0 голосов
/ 17 марта 2010

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

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

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

Обновление:
Попробуйте выполнить синхронизацию при вызове LoadComponent и посмотрите, сохраняется ли проблема.

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...