Словарь не является поточно-ориентированным вообще , независимо от того, добавляете ли вы его только или нет - в нем есть несколько внутренних структур, которые необходимо синхронизировать (особенно, когда внутренние хешбакетыизменить размер).
Вы должны либо внедрить собственную блокировку для любой операции над ней, либо, если вы находитесь в .Net 4.0, вы можете использовать новый ConcurrentDictionary - который является абсолютно фантастическим - и который полностью потоков-safe.
Другой вариант (обновление)
Тем не менее, есть еще один метод, который вы можете использовать - но он потребует некоторой настройки в зависимости от типа данных, которые вы вставляетев ваш словарь и убедитесь, что все ваши ключи гарантированно уникальны:
Дайте каждому потоку свой собственный частный словарь, в который он будет вставлять.
Когда каждый поток завершится, соберите все словари вместе и объедините ихв больший;как вы обрабатываете дубликаты ключей, зависит только от вас.Например, если вы кэшируете списки элементов по ключу, то вы можете просто объединить каждый список с одним и тем же ключом в один и поместить его в основной словарь.
Официальный ответ re: performance (после того, как вы приняли)
Итак, как говорится в ваших комментариях, вам нужна идея наилучшего метода (блокировка или слияние) для производительности и т. Д. Я не могу сказать вам, что это будет;в конечном итоге это необходимо будет сравнить.Я посмотрю, смогу ли я предложить какое-нибудь руководство, хотя:)
Во-первых - если вы знаете, сколько элементов в конечном итоге понадобится вашему Dictionar (s), используйте конструктор (int)
, чтобы минимизировать изменение размера.
Операция слияния, вероятно, будет лучшей;поскольку ни один из потоков не будет мешать друг другу.Если процесс, когда два объекта совместно используют один и тот же ключ, не будет слишком длинным;в этом случае принудительное выполнение всего этого в одном потоке в конце операции может привести к обнулению всего выигрыша в производительности путем распараллеливания первого этапа!
Точно так же могут возникнуть проблемы с памятью, поскольку вы будете эффективноклонирование словаря, поэтому, если конечный результат достаточно велик, вы можете в итоге потреблять много ресурсов;хотя и предоставлено - они будут освобождены.
Если в случае, когда ключ уже существует, необходимо принять решение на уровне потока, тогда вам понадобится конструкция lock () {}.
По словарю это обычно принимает следующую форму:
readonly object locker = new object();
Dictionary<string, IFoo> dictionary = new Dictionary<string, IFoo>();
void threadfunc()
{
while(work_to_do)
{
//get the object outside the lock
//be optimistic - expect to add; and handle the clash as a
//special case
IFoo nextObj = GetNextObject(); //let's say that an IFoo has a .Name
IFoo existing = null;
lock(locker)
{
//TryGetValue is a god-send for this kind of stuff
if(!dictionary.TryGetValue(nextObj.Name, out existing))
dictionary[nextObject.Name] = nextObj;
else
MergeOperation(existing, nextObject);
}
}
}
Теперь, если MergeOperation
, действительно медленный;тогда вы можете рассмотреть возможность снятия блокировки, создания клонированного объекта, который представляет собой объединение существующего и нового объекта, а затем повторное получение блокировки.Однако вам нужен надежный способ проверки того, что состояние существующего объекта не изменилось между первой блокировкой и второй (для этого полезен номер версии).