Многопоточность, DataReaders & Bulk Inserts ... Может ли это приложение быть многопоточным? - PullRequest
1 голос
/ 03 февраля 2012

Можно ли извлечь фрагмент кода внутри моего кода и запустить его в несколько потоков?

Приложение копирует данные из базы данных FoxPro на наш сервер SQL по сети (файлы довольно большие, поэтому массовое копирование должно выполняться с приращением ...

Это работает, но я бы хотел немного увеличить скорость.

1) При наличии помеченного мной раздела, работающего в нескольких потоках, ИЛИ в качестве альтернативы,

2) не проходить через каждый столбец в датарове,

Я выбрал второй вариант ... (обновленный код ниже)

КОД

private void BulkCopy(OleDbDataReader reader, string tableName, Table table)
    {
        if (Convert.ToBoolean(ConfigurationManager.AppSettings["CopyData"]))
        {
            Console.WriteLine(tableName + " BulkCopy Started.");
            try
            {
                DataTable tbl = new DataTable();
                foreach (Column col in table.Columns)
                {
                    tbl.Columns.Add(col.Name, ConvertDataTypeToType(col.DataType));
                }

                int batch = 1;
                int counter = 0;

                DataRow tblRow = tbl.NewRow();

                while (reader.Read())
                {
                    counter++;                  
 ////This section changed
                        object[] obj = tblRow.ItemArray;
                        reader.GetValues(obj);
                        tblRow.ItemArray = obj;
////**********
                    tbl.LoadDataRow(tblRow.ItemArray, true);

                    if (counter == BulkInsertIncrement)
                    {
                        Console.WriteLine(tableName + " :: Batch >> " + batch);
                        counter = PerformInsert(tableName, tbl, batch);
                        batch++;
                    }
                }

                if (counter > 0)
                {
                    Console.WriteLine(tableName + " :: Batch >> " + batch);
                    PerformInsert(tableName, tbl, counter);
                }

                tbl = null;
                Console.WriteLine("BulkCopy Success!");
            }
            catch (Exception)
            {
                Console.WriteLine("BulkCopy Fail!");
            }
            finally
            {
                reader.Close();
                reader.Dispose();
            }
            Console.WriteLine(tableName + " BulkCopy Ended.");
        }
    }

UPDATE Я пошел за вторым вариантом

Я не знал, что, находясь внутри цикла while (reader.Read ()), я могу сделать следующее. Мне не помогли значительно повысить производительность приложений

while (reader.Read())
{
  object[] obj = tblRow.ItemArray;
  reader.GetValues(obj);
  tblRow.ItemArray = obj;
  tbl.LoadDataRow(tblRow.ItemArray, true);
}

Ответы [ 4 ]

4 голосов
/ 03 февраля 2012

Нет необходимости в многопоточности, если вы устраняете ошибки новичка, которые делаете.Тонны медленного кода везде.

tblRow [col.Name] = reader [col.Name];

МЕДЛЕННО.НИКОГДА не используйте имя - получите индекс вне цикла, затем используйте индексы.В этой строке есть 2 (!) Словарных поиска для каждой строки, что занимает больше времени, чем обработка строки.

DataTables / DataSet начинать очень медленно (неудачный выбор по трихнологии), но подобный код действительно замедляет работу.Используйте профилировщик, чтобы увидеть другие плохие элементы.

1 голос
/ 03 февраля 2012

Возможно, это не тот ответ, который вам нужен, но пытались ли вы сначала запустить консольное приложение в режиме выпуска, используя всего одну инструкцию try и используя индексы в считывателе?Вероятно, это не приведет к значительному увеличению скорости, поскольку станет многопоточным, поскольку SQL Server будет основным узким местом.

Конечно, если вас не слишком заботит целостность данных (например, ваши идентификаторы)не являются последовательными) вы можете изменить тип блокировки таблицы для вставок и увеличить число потоков в 3-4, чтобы считывать определенные точки таблицы.

0 голосов
/ 03 февраля 2012

Если вы используете .NET 4, вы можете попробовать использовать TPL и преобразовать цикл foreach во что-то вроде

Parallel.ForEach(table.Columns, col => {/*rest of function here */}
0 голосов
/ 03 февраля 2012

Я не думаю, что ваш сценарий использования значительно выиграет от параллели для каждого.Также было бы довольно трудно реализовать причину OleDbReader, который используется в вашем коде.

Но что вы можете сделать, это запланировать вставки в новый поток, который ваш цикл не будет блокировать на время, пока SQL Serverнеобходимо вставить ваши данные.

Для этого вы можете использовать метод Task.Factory.StartNew() .Но это сделает обработку ошибок немного более сложной, с точки зрения того, что при неудачной вставке вы могли обработать больше данных или в худшем случае уже есть другой поток, ожидающий новых вставок для базы данных.

...