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