Иногда я получаю следующую ошибку при вызове ConcurrentDictionary.ToArray. Ошибка ниже:
System.ArgumentException: индекс равен или больше длины массива, или количество элементов в словаре больше, чем доступное пространство от индекса до конца целевого массива. в массиве System.Collections.Concurrent.ConcurrentDictionary
2.System.Collections.Generic.ICollection<System.Collections.Generic.KeyValuePair<TKey,TValue>>.CopyTo(KeyValuePair
2[], индекс Int32) в источнике System.Linq.Buffer1..ctor(IEnumerable
1) в System.Linq.Enumerable.ToArray[TSource](IEnumerable1 source) at ...Cache.SlidingCache
2.RemoveExcessAsync(состояние объекта) в ...\SlidingCache.cs :строка 141 в System.Threading.ExecutionContext.RunInternal(ExecutionContext ExecutionContext, обратный вызов ContextCallback, состояние объекта, логическое значение saveSyncCtx) в System.Threading.ExecutionContext.Run(ExecutionContext executeContext, обратный вызов ContextCallback, состояние объекта, логическое значение saveSyncCtx) в System.Threading. QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem() в System.Threading.ThreadPoolWorkQueue.Dispatch()
Я заметил, что в многопоточных сценариях вы иногда получаете исключения при сортировке ConcurrentDictionary. См. вопрос о переполнении стека здесь. Поэтому я начал использовать ConcurrentDictionary.ToArray перед сортировкой. Похоже, что при создании массива все еще есть проблемы.
Параллельный словарь используется для кэша, который поддерживает объекты и сбрасывает последние объекты, к которым обращались, когда достигается установленное максимальное количество элементов для кэша. К кешу обращаются несколько потоков, и вышеуказанная ошибка возникает при попытке удалить старые элементы, чтобы в массив можно было добавить новые элементы. Пожалуйста, смотрите некоторые фрагменты кода ниже:
public class SlidingCache<TKey, TValue> : IDictionary<TKey, TValue>
{
public int MinCount { get; private set; }
public int MaxCount { get; private set; }
private readonly IDictionary<TKey, CacheValue> _cache = new ConcurrentDictionary<TKey, CacheValue>();
public SlidingCache(int minCount=75000, int maxCount=100000)
{
if (minCount <= 2)
throw new ArgumentException("minCount");
if (maxCount <= minCount)
throw new ArgumentException("maxCount");
MinCount = minCount;
MaxCount = maxCount;
}
#region IDictionary<TKey, TValue>
public int Count
{
get { return _cache.Count; }
}
public TValue this[TKey key]
{
get
{
return _cache[key].Value;
}
set
{
_cache[key]=new CacheValue(value);
RemoveExcess();
}
}
...
#endregion
private void RemoveExcess()
{
if (this.Count <= this.MaxCount || Interlocked.Increment(ref _removingExcess) != 1)
return;
ThreadPool.QueueUserWorkItem(RemoveExcessAsync, null);
}
private int _removingExcess;
private void RemoveExcessAsync(object state)
{
var remove = _cache.ToArray().OrderByDescending(i => i.Value.LastRequestTime).Take(MaxCount - MinCount);
foreach (var pair in remove)
{
_cache.Remove(pair.Key);
}
Interlocked.Exchange(ref _removingExcess, 0);
}
Может ли кто-нибудь объяснить потенциальную причину вышеуказанного исключения и какие-либо обходные пути?
Спасибо.