Вложенный параллельный цикл показывает, что индекс был за пределами границы при добавлении значения в список

У меня есть List-A, чтобы заполнить List-B большим количеством значений, поступающих из базы данных, и заполняет другой List-C. Я использую параллельный цикл и обычный цикл foreach. Если я использую только цикл foreach, для заполнения List-C требуется около 19 минут, но при использовании параллельного цикла для заполнения того же списка требуется всего 3 минуты.

Но почему-то при добавлении List-B в List C выдает ошибку «Индекс вышел за пределы массива», без информации

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

person messed-up    schedule 19.11.2015    source источник
comment
Variances.Add не является потокобезопасным внутри параллельного foreach. рассмотрите возможность использования оператора блокировки или параллельных коллекций, таких как concurrent bag   -  person M.kazem Akhgary    schedule 19.11.2015
comment
Кажется, сейчас работает, спасибо @dasblinkenlight   -  person messed-up    schedule 19.11.2015
comment
Больше нет ошибок, но некоторые значения заполняются в случайных местах для некоторых элементов.   -  person messed-up    schedule 19.11.2015


Ответы (1)


Вы получаете ошибку выхода за пределы, потому что вы добавляете элементы в растущий список из нескольких потоков. Один поток пытается расширить массив, а другой обращается к старому.

Вы можете решить эту проблему, заменив Parallel.ForEach на AsParallel и Select, например:

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();

Больше нет ошибок, но некоторые значения заполняются в случайных местах для некоторых элементов.

По умолчанию параллельный выбор не сохраняет исходный порядок. Вы можете исправить это, добавив .AsOrdered() после AsParallel(), но есть вероятность, что производительность снизится.

person Sergey Kalinichenko    schedule 19.11.2015