Parallel.For List <Int32>

I wrote the following lines of code:

    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;
    }

      

Then I wrote this block of code:

    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 works fine, Parallel throws this exception:

System.ArgumentException was not handled by user code

The destination array was not long enough. Check destIndex and the length and bottom bounds of the array.

Anyone have an idea why?

+3


source to share


2 answers


You are using List <> which is not thread safe. Use ConcurrentBag <>. I run into this all the time when switching to parallel loops. This will happen periodically, not every time, so it is difficult to detect.



+5


source


Yes, List<>

it is not thread safe. But you can parallelize this by first assigning the list to your desired size and then using [] (index) to fill in the values.

In other words, don't expand your array in parallel. Grow it at the beginning, then fill it in parallel.

EDIT:

Additional Information.

List<>

is a simple array that can grow for convenience. So if you preselect it (increase it to LENGTH 1000 from the beginning), the size ( .Count

) will not change during filling.

So, if Parallel.For uses any type of partitioning, it will assign different indices to each thread that will process the loop. And each ELEMENT of the array will be filled exactly ONLY.

I don't see any problem with this, other than Random.Next()

it is not thread safe and can return gibberish. But, that should do it, right?



NEW EDIT:

More information:

If using List <> is a requirement, preallocation would mean:

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

      

If you can use SWITCH types for something else, I would recommend an array for that:

int[] myList=new int[MY_COUNT];
//  proceed with Parallel.For

      

+1


source







All Articles