У меня есть база данных SQL Server с 500 000 записей в таблице main
. Существуют также три таблицы, которые называются child1
, child2
и child3
. Множество-много отношений между child1
, child2
, child3
и main
реализованы через три таблицы отношений: main_child1_relationship
, main_child2_relationship
и main_child3_relationship
. Мне нужно прочитать записи в main
, обновить main
, а также вставить в таблицы отношений новые строки, а также вставить новые записи в дочерние таблицы. Записи в дочерних таблицах имеют ограничения уникальности, поэтому псевдокод для фактического вычисления (CalculateDetails) будет выглядеть примерно так:
for each record in main
{
find its child1 like qualities
for each one of its child1 qualities
{
find the record in child1 that matches that quality
if found
{
add a record to main_child1_relationship to connect the two records
}
else
{
create a new record in child1 for the quality mentioned
add a record to main_child1_relationship to connect the two records
}
}
...repeat the above for child2
...repeat the above for child3
}
Это прекрасно работает как однопоточное приложение. Но это слишком медленно. Обработка на С# довольно тяжелая и занимает слишком много времени. Я хочу превратить это в многопоточное приложение.
Каков наилучший способ сделать это? Мы используем Linq для Sql.
До сих пор мой подход заключался в создании нового объекта DataContext
для каждой партии записей из main
и использования ThreadPool.QueueUserWorkItem
для его обработки. Однако эти партии наступают друг на друга, потому что один поток добавляет запись, а затем следующий поток пытается добавить один и тот же... Я получаю всевозможные интересные блокировки SQL Server.
Вот код:
int skip = 0;
List<int> thisBatch;
Queue<List<int>> allBatches = new Queue<List<int>>();
do
{
thisBatch = allIds
.Skip(skip)
.Take(numberOfRecordsToPullFromDBAtATime).ToList();
allBatches.Enqueue(thisBatch);
skip += numberOfRecordsToPullFromDBAtATime;
} while (thisBatch.Count() > 0);
while (allBatches.Count() > 0)
{
RRDataContext rrdc = new RRDataContext();
var currentBatch = allBatches.Dequeue();
lock (locker)
{
runningTasks++;
}
System.Threading.ThreadPool.QueueUserWorkItem(x =>
ProcessBatch(currentBatch, rrdc));
lock (locker)
{
while (runningTasks > MAX_NUMBER_OF_THREADS)
{
Monitor.Wait(locker);
UpdateGUI();
}
}
}
И вот ProcessBatch:
private static void ProcessBatch(
List<int> currentBatch, RRDataContext rrdc)
{
var topRecords = GetTopRecords(rrdc, currentBatch);
CalculateDetails(rrdc, topRecords);
rrdc.Dispose();
lock (locker)
{
runningTasks--;
Monitor.Pulse(locker);
};
}
И
private static List<Record> GetTopRecords(RecipeRelationshipsDataContext rrdc,
List<int> thisBatch)
{
List<Record> topRecords;
topRecords = rrdc.Records
.Where(x => thisBatch.Contains(x.Id))
.OrderBy(x => x.OrderByMe).ToList();
return topRecords;
}
CalculateDetails
лучше всего объясняется псевдокодом наверху.
Я думаю, что должен быть лучший способ сделать это. Пожалуйста помоги. Большое спасибо!