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