Исключение индекса вне диапазона при использовании параллельного цикла for - PullRequest
6 голосов
/ 24 октября 2010

Я пытаюсь выполнить следующий код и продолжаю получать исключение индекса вне диапазона при попытке присвоить значениям массива в списке: -

        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]));

Я что-то здесь не так делаю?Я понимаю, что процесс неупорядоченный / асинхронный, но почему «i» получает значения, которые больше значения «array.Length»?

1 Ответ

18 голосов
/ 24 октября 2010

Проблема в том, что вы не можете вызвать 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-элементного массива.

...