Подтвердить что ты не робот

При каких обстоятельствах 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.

Я понятия не имею, что здесь происходит. Вы?

4b9b3361

Ответ 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)