我有一个使用EF进行数据访问的MVC网站。该应用程序接收数据,运行一系列计算并存储结果。每批数据可以有几千条记录,计算平均需要30秒 - 我想在后台运行所有这些。
到目前为止,我已经安装了Hangfire以触发批次。然后我做了:
var queue = new Queue<MyItem>();
// queue is populated ...
while (queue.Any())
{
var item = queue.Dequeue();
var task = Task.Run(() =>
{
using (var context = new MyDbContext())
{
context.MyItem.Add(item);
// Run Calculations
try {
context.SaveChanges();
}
catch {
// Log error
}
}
}
}
当批处理正在运行时,站点会变得完全没有响应,或者我收到“基础提供程序在打开时失败”错误。
有更好的方法吗?
答案 0 :(得分:5)
您似乎正在使用Task.Run
创建任务而不是等待它们完成。这意味着您将为队列中的每个项生成一个任务,该任务将在不同的ThreadPool
个线程上并发运行。这可能是一种负担,可能(并且可能确实)影响您的常规请求。
您应该以某种方式限制这些任务的并发性。最简单的IMO使用的是TPL Dataflow的ActionBlock
。您可以使用委托和选项(例如MaxDegreeOfParallelism
)创建块,将项目发布到其中并等待它完成:
block = new ActionBlock<MyItem>(item =>
{
using (var context = new MyDbContext())
{
context.MyItem.Add(item);
// Run Calculations
try {
context.SaveChanges();
}
catch {
// Log error
}
}
}, new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 2 });
while (queue.Any())
{
var item = queue.Dequeue();
block.Post(item);
}
block.Complete();
await block.Completion;