Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Parallel For Loop - Problems when adding to a List

I am having some issues involving Parallel for loops and adding to a List. The problem is, the same code may generate different output at different times. I have set up some test code below. In this code, I create a List of 10,000 int values. 1/10th of the values will be 0, 1/10th of the values will be 1, all the way up to 1/10th of the values being 9.

After setting up this List, I setup a Parallel for loop that iterates through the list. If the current number is 0, I add a value to a new List. After the Parallel for loop completes, I output the size of the list. The size should always be 1,000. Most of the time, the correct answer is given. However, I have seen 3 possible incorrect outcomes occur:

  1. The size of the list is less than 1,000
  2. An IndexOutOfRangeException occurs @ doubleList.Add(0.0);
  3. An ArgumentException occurs @ doubleList.Add(0.0);

The message for the ArgumentException given was: Destination array was not long enough. Check destIndex and length, and the array's lower bounds.

What could be causing the errors? Is this a .Net bug? Is there something I can do to prevent this from happening?

Please try the code for yourself. If you do not get an error, try it a few times. Please also note that you probably will not see any errors using a single-core machine.

using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace ParallelTest
{
    class Program
    {
        static void Main(string[] args)
        {
            List<int> intList = new List<int>();
            List<double> doubleList = new List<double>();

            for (int i = 0; i < 250; i++)
            {
                intList.Clear();
                doubleList.Clear();

                for (int j = 0; j < 10000; j++)
                {
                    intList.Add(j % 10);
                }

                Parallel.For(0, intList.Count, j =>
                {
                    if (intList[j] == 0)
                    {
                        doubleList.Add(0.0);
                    }
                });

                if (doubleList.Count != 1000)
                {
                    Console.WriteLine("On iteration " + i + ": List size = " + doubleList.Count);
                }
            }

            Console.WriteLine("\nPress any key to exit.");
            Console.ReadKey();
        }
    }
}
like image 783
Kevin Crowell Avatar asked May 02 '10 03:05

Kevin Crowell


1 Answers

I expect that System.Collections.Generic.List is not thread-safe, meaning that if you try to concurrently Add from two different threads, things will go wrong. Ah yes, it says so in the docs.

You could prevent this from happening in a number of ways:

  • use a collection type that allows threadsafe adds (there are some new ones in .Net 4.0)
  • lock before you add
  • use thread-local storage for the collections, and merge them at the end
  • ...

These are very typical issues you encounter with data parallel code.

like image 196
Brian Avatar answered Sep 24 '22 02:09

Brian