Во-первых: Не используйте DataTable
для таких операций, как эти:
- это медленно
- это отнимает слишком много памяти
- и вам нужно долго ждать, прежде чем вы сможете начать обработка данных
- За это время дополнительные ядра ничего не делают, поскольку считывание данных в
DataTable
не является параболическим.
- Кроме того, при чтении данных, как правило, почти используется ЦП, поскольку основной причиной является сеть или другая задержка ввода-вывода.
Итак, еще раз: Не используйте DataTable
для таких операций, как это.
Вместо этого используйте DataReader
. Это позволяет сразу же начать потреблять/обрабатывать данные, а не ждать, пока он будет загружен. Простейший вариант будет (пример для MS SQL Server):
var command = new SqlCommand()
{
CommandText = "SELECT * FROM Table";
Connection = new SqlConnection("InsertConnectionString");
};
using(var reader = command.ExecuteReader())
{
while(reader.Read())
{
var values = new object[reader.FieldCount];
reader.GetValues(values);
// process values of row
}
}
читатель будет заблокирован во время выполнения кода обработки, то есть не больше строк считываются из БД.
Если код обработки является тяжелым, может потребоваться использовать библиотеку Task
для создания задач, которые выполняют проверку, что позволит вам использовать несколько ядер. Тем не менее, есть накладные создания Task
, если один Task
не содержит достаточного количества «работы» вы можете партия пару строк вместе:
public void ReadData()
{
var taskList = new List<Task<SomeResultType>>();
var command = new SqlCommand()
{
CommandText = "SELECT * FROM Table";
Connection = new SqlConnection("InsertConnectionString");
};
using(var reader = command.ExecuteReader())
{
var valueList = new List<object[]>(100);
while(reader.Read())
{
var values = new object[reader.FieldCount];
reader.GetValues(values);
valueList.Add(values);
if(valueList.Count == 100)
{
var localValueList = valueList.ToList();
valueList.Clear();
taskList.Add(Task<SomeResultType>.Factory.StartNew(() => Process(localValueList));
}
}
if(valueList.Count > 0)
taskList.Add(Task<SomeResultType>.Factory.StartNew(() => Process(valueList));
}
// this line completes when all tasks are done
Task.WaitAll(taskList.ToArray());
}
public SomeResultType Process(List<object[]> valueList)
{
foreach(var vals in valueList)
{
// put your processing code here, be sure to synchronize your actions properly
}
}
- Пакетный размер (в настоящее время 100) зависит от фактической обработка и, возможно, потребуется скорректировать.
- Synchronizing держит это собственные проблемы, вы должны быть очень осторожны, об общих ресурсах
Хорошее объяснение Но у меня есть некоторые вопросы: 1, что было бы лучше, решение, которое вы предложили, или создание нескольких задач, каждый из которых использует SQLReader для чтения ряда строк из базы данных (например,основанный на пейджинге), особенно в случае обработки результатов, не очень тяжелый. 2-есть ли существенная разница в производительности между Task BackgroundWork? –