2015-12-18 100 views
4

我有一個使用EF進行數據訪問的MVC網站。該應用程序接收數據,運行一系列計算並存儲結果。每批數據可以有幾千條記錄,計算平均需要30秒 - 我想在後臺運行所有這些。在MVC5網站批處理

到目前爲止,我已經使用了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

做你的網站需要用戶等待響應?你可以運行計算,然後在結果準備好時通知他們(如電子郵件)嗎? – Jasen

+0

如果'MyDbContext'支持異步,則應該使用'await context.SaveChangesAsync()'。這可以釋放你的線程來處理更多的請求。 – i3arnon

+0

@Jasen無需用戶通知。目前有一個等待Task.WhenAll()進行清理和日誌記錄。 – Neil

回答

3

看來您正在使用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; 
+0

爲什麼選擇2代表MaxDegreeOfParallelism,而不是maxdegreeofparallelism = environment.processorcount?說明性的目的? –

+0

@BigDaddy這只是一個例子。我甚至會用1來看看是否足夠好,以便及時完成背景批次。如果計算主要受CPU限制,「ProcessorCount」可以佔用所有可用的CPU。 – i3arnon

+0

@ i3arnon太好了。計算量很大 - 我使用MaxDegreeOfParalellism在1,2和Environment.ProcessorCount(4)上運行,它似乎對後者最爲滿意。確定正確價值的一般邏輯是什麼? – Neil