Обновление
Как я только что заметил, вы упомянули в комментарии, что проблема вызвана математическим вычислением.
Будет лучше отделить часть вычисления иОбновление БД.
Для расчетной части используйте Parallel.ForEach()
, чтобы оптимизировать свою работу, и вы можете контролировать номер потока.
И только после того, как все эти задачи завершены.Используйте async-await
для обновления ваших данных в БД без SemaphoreSlim
, о котором я упоминал.
public static async Task<int> Work()
{
var id = await CreateIdInDB() // async create record in DB
// run background task, don't wait when it finishes
Task.Run(async () => {
//Calculation Part
ConcurrentBag<int> data = new ConcurrentBag<int>();
Parallel.ForEach(
listOfData,
new ParallelOptions { CancellationToken = token, MaxDegreeOfParallelism = 3 },
x => {ConcurrentBag.Add(calculationPart(x))});
//Update DB part
int[] data_arr = data.ToArray();
List<Task> worker = new List<Task>();
foreach (var i in data_arr)
{
worker.Add(DBPart(x));
}
await Task.WhenAll(worker);
});
// return created id immediately
return id;
}
Убедитесь, что все они начинаются вместе, так как вы используете async-await
в Parallel.forEach
.
Сначала прочитайте об этом вопросе для 1-го и 2-го ответов.Объединять эти два смысла бессмысленно.
На самом деле async-await
максимизирует использование доступного потока, поэтому просто используйте его.
public static async Task<int> Work()
{
var id = await CreateIdInDB() // async create record in DB
// run background task, don't wait when it finishes
Task.Run(async () => {
List<Task> worker = new List<Task>();
foreach (var i in listOfData)
{
worker.Add(ProcessSingle(x));
}
await Task.WhenAll(worker);
});
// return created id immediately
return id;
}
Но тогда возникает другая проблема, в этом случае эти задачи все еще начинаются все вместе, съедаяиспользование вашего процессора.
Чтобы избежать этого, используйте SemaphoreSlim
public static async Task<int> Work()
{
var id = await CreateIdInDB() // async create record in DB
// run background task, don't wait when it finishes
Task.Run(async () => {
List<Task> worker = new List<Task>();
//To limit the number of Task started.
var throttler = new SemaphoreSlim(initialCount: 20);
foreach (var i in listOfData)
{
await throttler.WaitAsync();
worker.Add(Task.Run(async () =>
{
await ProcessSingle(x);
throttler.Release();
}
));
}
await Task.WhenAll(worker);
});
// return created id immediately
return id;
}
Подробнее Как ограничить количество одновременных операций асинхронного ввода-вывода? .
Кроме того, не используйте Task.Factory.StartNew()
, когда простого Task.Run()
достаточно для выполнения желаемой работы, прочитайте эту превосходную статью Стивена Клири.