Parallel.For List <int32> </int32>

时间:2012-03-07 18:45:02

标签: .net task-parallel-library

我写了以下几行代码:

    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,以及数组的下限。

任何人都知道为什么?

2 个答案:

答案 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