Índice de exceção fora do alcance ao usar paralelo para loop
-
25-09-2019 - |
Pergunta
Estou tentando executar o código a seguir e continuo obtendo um índice fora da exceção do alcance ao tentar atribuir valores de matriz à lista:-
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]));
Estou fazendo algo errado aqui? Entendo que o processo não é ordenado/assíncrono, mas por que "eu" obtém valores mais altos que o valor de "Array.length"?
Solução
O problema é que você não pode ligar List.Add()
simultaneamente em vários threads. Se você precisar de coleções seguras de tópicos, consulte o System.Collections.Concurrent
espaço para nome.
Se você invadir o depurador quando tiver uma exceção, verá que verá que i
é não Maior que array.Length
, mas é um poder de 2 que é substancialmente menor que array.Length
. O que acontece é que o List
Começa com uma variedade vazia de algo como 4 elementos. Sempre que você adiciona um elemento a uma lista cuja matriz está cheia, ela cria uma variedade de duas vezes o comprimento da matriz antiga, copia os elementos antigos e armazena a nova matriz.
Agora, digamos que sua lista está até 31 elementos (o que significa que tem espaço para mais um) e dois threads tentam adicionar um 32º elemento. Ambos executarão o código como este:
if (_size == _items.Length)
{
EnsureCapacity(_size + 1);
}
_items[_size++] = item;
Primeiro, ambos verão isso _size
(31) não é _items.Length
(32), então os dois executam _size++
. O primeiro tópico receberá 31 (o índice correto do 32º elemento) e mudará _size
para 32. O segundo tópico receberá 32 e tentará indexar _items[32]
, o que dá sua exceção porque está tentando acessar o 33º elemento de uma matriz de 32 elementos.