При каких обстоятельствах System.Collections.ArrayList.Add генерирует IndexOutOfRangeException?
Мы испытываем странную ошибку в производственной среде, которую мы не можем отлаживать и не вводить код регистрации. Я пытаюсь понять это, но следы стека путают меня.
System.IndexOutOfRangeException: Index was outside the bounds of the array.
at System.Collections.ArrayList.Add(Object value)
at ...
В соответствии с MSDN Add
метод должен только бросать NotSupportedException
.
Я понятия не имею, что здесь происходит. Вы?
Ответы
Ответ 1
Это сводится к тому, что List не является потокобезопасным. У меня было IndexOutOfRangeException, возникающее при повторении списка после добавления элементов с использованием нескольких потоков без синхронизации, как показано ниже,
List<TradeFillInfo> updatedFills = new List<TradeFillInfo>();
Parallel.ForEach (trades, (trade) =>
{
TradeFillInfo fill = new TradeFillInfo();
//do something
updatedFills.Add(fill); //NOTE:Adding items without synchronization
});
foreach (var fill in updatedFills) //IndexOutOfRangeException here sometimes
{
//do something
}
В этом случае updateFills count повреждается, а последующая итерация терпит неудачу. Обертка Add() вокруг оператора блокировки должна предотвратить это.
lock (updatedFills)
{
updatedFills.Add(fill);
}
Ответ 2
IndexOutOfRangeException
вызывается, когда "делается попытка получить доступ к элементу массива с индексом, который выходит за пределы массив."
Обратите внимание, что класс ArrayList
не является потокобезопасным. Возможно, что в многопоточных сценариях условия гонки приведут к тому, что ArrayList
попытается прочитать/записать в массив поддержки по индексам, которые находятся за пределами его диапазона.
Пример. Один поток уменьшает размер массива поддержки (возможно, через вызов TrimToSize
) в то же время, когда добавляется другой поток в коллекцию. Теперь, если массив поддержки находится в полной емкости, добавляющий поток попытается расширить его емкость (путем выделения нового массива) для размещения нового элемента. Одновременный вызов TrimToSize
отменяет этот эффект. Затем, к тому времени, когда добавляющий поток попытается записать в массив, индекс, который, по его мнению, был доступен, больше не будет, в результате чего будет выбрано исключение.
Исправление: используйте поточно-безопасные конструкции, соответствующие вашей ситуации.
Ответ 3
Это почти наверняка проблема concurrency... У вас, вероятно, есть два потока, которые изменяют коллекцию одновременно, а класс ArrayList
не предназначен для поддержки параллельного доступа. Состояние гонки происходит, что иногда приводит к тому, что один из потоков пытается записать в позиции за пределами массива.
Попробуйте защитить все обращения к коллекции с помощью операторов lock
или использовать синхронизированную оболочку коллекции (используя метод ArrayList.Synchronized
)