我写了以下几行代码:
private static List<Int32> GetRandomList_Serial()
{
List<Int32> returnValue = new List<int>();
Random random = new Random();
for (int i = 0; i < 10000000; i++)
{
returnValue.Add(random.Next());
}
returnValue.Sort();
return returnValue;
}
然后我写了这段代码:
private static List<Int32> GetRandomList_Parallel()
{
List<Int32> returnValue = new List<int>();
Random random = new Random();
Parallel.For(0, 10000000, y =>
{
returnValue.Add(random.Next());
});
returnValue.Sort();
return returnValue;
}
Serial工作正常,Parallel抛出此异常:
System.ArgumentException未被用户代码
处理目标数组不够长。检查destIndex和length,以及数组的下限。
任何人都知道为什么?
答案 0 :(得分:5)
您正在使用List&lt;&gt;这不是线程安全的。使用ConcurrentBag&lt;&gt ;.我一直遇到这种情况,同时切换到并行循环。它会间歇性地发生,而不是每次都发生,因此很难检测到。
答案 1 :(得分:1)
是的,List<>
不是线程安全的。但是,您可以通过将List预先分配到您想要的大小来并行化,然后使用[](索引)来填充值。
换句话说,不要并行增长数组。在开始时增长,然后并行填充。
编辑:
更多信息。
List<>
是一个简单的数组,可以为方便起见而增长。因此,如果您预先分配它(在开始时将其增长到1000的长度),它的大小(.Count
)在填充期间不会改变。
因此,如果Parallel.For使用任何类型的分区,它将为每个将处理循环的线程分配不同的索引。并且,阵列的每个ELEMENT将完全填充ONCE。
我没有看到任何问题,除了Random.Next()
不是线程安全的,可能会返回乱码。但是,它应该这样做,对吗?
新编辑:
更多信息:
如果使用List&lt;&gt;是一项要求,预先分配将意味着:
List<int> myList=new List<int>(MY_COUNT);
// very unefficient method of pre-allocation,
// but there is no other way to assign .Count
for (int n=0; n<MY_COUNT; n++) {
myList.Add(0);
}
// proceed with Parallel.For
如果您可以将类型切换为其他类型,我建议使用一个数组:
int[] myList=new int[MY_COUNT];
// proceed with Parallel.For