Большой вопрос: вам нужен foreach
, чтобы сделать снимок?
Если ответ «нет», тогда используйте ConcurrentDictionary
, и у вас, вероятно, все будет хорошо. (Единственный оставшийся вопрос заключается в том, что природа ваших вставок и чтений плохо влияет на полосатые блокировки, но если бы это было так, вы бы находили обычные операции чтения и записи в словарь еще хуже).
Однако, поскольку GetEnumerator
не предоставляет снимок, он не будет перечислять то же начало в начале, что и в конце. Это может пропустить предметы или дублировать предметы. Вопрос в том, для тебя это катастрофа или нет.
Если было бы катастрофой, если бы у вас были дубликаты, но не иначе, вы можете отфильтровать дубликаты с помощью Distinct()
(независимо от того, введены ли ключи на клавишах или ключ и значение, как требуется).
Если вам действительно нужен жесткий снимок, используйте следующий подход.
Иметь ConcurrentDictionary
(dict
) и ReaderWriterLockSlim
(rwls
). При чтении и записи получите блокировку чтения (да, даже если вы пишете):
public static void AddToDict(string key, string value)
{
rwls.EnterReadLock();
try
{
dict[key] = value;
}
finally
{
rwls.ExitReadLock();
}
}
public static bool ReadFromDict(string key, out string value)
{
rwls.EnterReadLock();
try
{
return dict.TryGetValue(key, out value);
}
finally
{
rwls.ExitReadLock();
}
}
Теперь, когда мы хотим перечислить словарь, мы получаем блокировку записи (даже если мы читаем):
public IEnumerable<KeyValuePair<string, string>> EnumerateDict()
{
rwls.EnterWriteLock();
try
{
return dict.ToList();
}
finally
{
rwls.ExitWriteLock();
}
}
Таким образом, мы получаем общую блокировку для чтения и записи, потому что ConcurrentDictionary
имеет дело с конфликтами, связанными с этим для нас. Мы получаем эксклюзивную блокировку для перечисления, но достаточно долго, чтобы получить снимок словаря в списке, который затем используется только в этом потоке и не используется совместно с другими.