messed-up
messed-up

Reputation: 533

Nested Parallel loop gives index was outside the bound while adding value to a list

I have a List-A to populate List-B with more value that comes from database and populates another List-C. I'm using parallel loop and normal foreach loop. If I use just foreach loop, it takes about 19 minutes to populate List-C, but using parallel loop it only takes 3 minutes to populate same list.

But for some reason, when adding List-B to List C, it gives out an error "Index was outside the bounds of the array", with no informations

List<Variance> Variances = new List<Variance>();
Parallel.ForEach(words, (word) =>
{
     List<wordConfig> configlist = new List<wordConfig>();
     foreach (VariancePopulationConfig value in values)
     {
         VariancePopulationConfig config = new VariancePopulationConfig(Named, Category, Schedule);

         using (SqlConnection con = new SqlConnection(ConnectionString))
         {
             con.Open();
             SqlCommand cmd = new SqlCommand();
             cmd.Connection = con;
             cmd.Parameters.AddWithValue("@word", word);
             cmd.Connection = con;
             cmd.CommandType = CommandType.Text;
             cmd.CommandText = value.SelectQuery;
             returnedvalue = cmd.ExecuteScalar().ToString();
             config.DestinationColOrdinal = value.DestinationColOrdinal;
             config.CurrentOrHistory = value.CurrentOrHistory;
             config.WordValue = returnedvalue.Equals("0") ? string.Empty : returnedvalue;
             config.Word = word;
         }
         configlist.Add(config);
     }
     Variances.Add(new Variance { Word = word, VarianceConfigs = configlist });
});

Upvotes: 1

Views: 683

Answers (1)

Sergey Kalinichenko
Sergey Kalinichenko

Reputation: 726939

You get an out-of-bound error because you are adding items to a growing list from multiple threads. One thread tries to expand the array, while the other is accessing the old one.

You can fix this problem by replacing Parallel.ForEach with AsParallel and Select, like this:

var Variances = words.AsParallel().Select((word) => {
     List<wordConfig> configlist = new List<wordConfig>();
     foreach (VariancePopulationConfig value in values) {
         VariancePopulationConfig config = new VariancePopulationConfig(Named, Category, Schedule);
         using (SqlConnection con = new SqlConnection(ConnectionString)) {
             con.Open();
             SqlCommand cmd = new SqlCommand();
             cmd.Connection = con;
             cmd.Parameters.AddWithValue("@word", word);
             cmd.Connection = con;
             cmd.CommandType = CommandType.Text;
             cmd.CommandText = value.SelectQuery;
             returnedvalue = cmd.ExecuteScalar().ToString();
             config.DestinationColOrdinal = value.DestinationColOrdinal;
             config.CurrentOrHistory = value.CurrentOrHistory;
             config.WordValue = returnedvalue.Equals("0") ? string.Empty : returnedvalue;
             config.Word = word;
         }
         configlist.Add(config);
     }
     return new Variance { Word = word, VarianceConfigs = configlist };
}).ToList();

No more error, but some values are populating at random places for some of the items.

By default parallel select does not preserve original ordering. You can fix this by adding .AsOrdered() after AsParallel(), but there is a chance that the performance would degrade.

Upvotes: 2

Related Questions