我有一个使用EF进行数据访问的MVC网站。该应用程序接收数据,运行一系列计算并存储结果。每一批数据可以有数千条记录,并且计算平均需要30秒-我想在后台运行所有这些。
到目前为止,我已经安装了Hangfire来触发批次。然后,我做:
var queue = new Queue(); // 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 } } } }
批处理正在运行时,网站要么变得完全无响应,要么收到“底层提供程序在打开时失败”错误。
有更好的方法吗?
似乎您正在使用Task.Run
而不是等待任务完成来创建任务。这意味着您将为队列中的每个项目生成一个任务,这些任务将同时在不同ThreadPool
线程上运行。这可能是一个很大的负担,可能会(并且确实会)影响您的常规请求。
您应该以某种方式限制这些任务的并发性。最简单的IMO是使用TPL Dataflow的ActionBlock
。您使用委托和选项(例如MaxDegreeOfParallelism
)创建块,将项目发布到其中并等待其完成:
block = new ActionBlock(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;