Индекс вне диапазона Исключение при использовании параллельного для петли
-
25-09-2019 - |
Вопрос
Я пытаюсь выполнить следующий код, и я продолжаю получать индекс из исключения диапазона при попытке назначить значения массива в список: -
int[] array = new int[1000000];
for (int i = 0; i < array.Length; i++)
{
array[i] = i;
}
List<int> list = new List<int>();
Parallel.For(0, array.Length, i => list.Add(array[i]));
Я здесь делаю что-то не так? Я понимаю, что процесс неупорядован / асинхронный, но почему «я» получаю значения, которые выше, чем значение «Array.Length»?
Решение
Проблема в том, что вы не можете позвонить List.Add()
одновременно на нескольких потоках. Если вам нужны косвенные сборы, см. System.Collections.Concurrent
пространство имен.
Если вы нарушитесь в отладчик, когда вы получите исключение, вы увидите, что i
является нет лучше чем array.Length
, но вместо этого является мощность 2, которая существенно меньше, чем array.Length
. Отказ Что происходит, это то, что List
Начинается с пустым массивом чего-то вроде 4 элемента. Всякий раз, когда вы добавляете элемент в список, массив которого заполнен, он создает массив в два раза длины старого массива, копирует к нему старые элементы и сохраняет новый массив.
Теперь скажем, что ваш список до 31 элемента (значение у него есть место для еще одного) и два потока пытаются добавить 32-й элемент. Они оба будут выполнять код, как это:
if (_size == _items.Length)
{
EnsureCapacity(_size + 1);
}
_items[_size++] = item;
Сначала они оба считают, что _size
(31) не _items.Length
(32), поэтому они оба выполняют _size++
. Отказ Первый поток получит 31 (правильный индекс 32-х элемента) и изменить _size
до 32. Второй поток получит 32 и пытается индексировать _items[32]
, что дает вам ваше исключение, потому что он пытается получить доступ к 33-ю элементу 32-элементного массива.