我正在尝试执行以下代码,并且在尝试将数组值分配给列表时,我不断收到索引超出范围的异常: -
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”的值?
答案 0 :(得分:18)
问题是您无法在多个线程上同时调用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]
,这会为您提供异常,因为它正在尝试访问32元素数组的第33个元素。