indexoutofrangeexception в Datatable с Parallel.Foreach

Я пытаюсь дополнить DataTable с IP-адресами в одном столбце с их обратным отображением DNS. Я получаю этот DataTable откуда-то еще. Затем я экспортирую эту таблицу в SQL Server, используя SQLBulkcopy

Я добавляю два столбца, один для имени DNS и один только для части домена верхнего уровня. Так как у меня много IP-адресов, и обратный DNS занимает некоторое время, я использую Parallel для каждого. Странно, но я получаю странные непредсказуемые исключения IndexOutOfRangeException в NestedException для параллельного цикла (или иногда вне параллельного цикла, когда я вызываю clear в datatable). Зачем?

Вот что я делаю

        //mapping specifies a mapping within the DataTable and the Database

        SqlBulkCopy copy = new SqlBulkCopy(cn);
        foreach (KeyValuePair<int, int> pair in mapping)
        {                                                                       
            copy.ColumnMappings.Add(pair.Key, pair.Value);
        }


        dt.Columns.Add("URL");
        dt.Columns.Add("Domain");
        solver.AddDNSInfo(dt, 1); //second row has the IP
        copy.WriteToServer(dt);
        dt.Clear();     //exceptions are thrown here   




    //ipIndex is the index within the datatable where the IP of interest is. 
    //In my scenario ipIndex=1
    public void AddDNSInfo(DataTable table, int ipIndex)
    {
        Parallel.ForEach(table.AsEnumerable(), row =>
        {
                string URL = GetDNSName((string)row[ipIndex]);
                row["URL"] = URL; //exceptions are thrown here
                row["Domain"] = GetTopLevelDomain(URL);
                Console.Write("*");
        });

1 ответ

Решение

Так как DataTable не является потокобезопасным для многопоточной операции записи.

MSDN говорит:

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

Вы пишете новые столбцы с несколькими потоками одновременно, когда в Parallel.ForEach, ты сделаешь:

  row["URL"] = URL;
  row["Domain"] = GetTopLevelDomain(URL);

Вам нужно синхронизировать любой письменный вызов с вашим DataTable (используя замок или какой-либо вид монитора)

Другие вопросы по тегам