2012-04-14 43 views
39

.net函數Parallel.ForEach是否阻塞調用線程?我對這種行爲的猜測是其中之一:Parallel.ForEach塊嗎?

  1. 是的,它阻塞,直到最慢的項目執行返回。
  2. 不,它不會立即阻止並返回控件。並行運行的項目是在後臺線程上完成的。

或者也許別的事情正在發生,任何人都知道肯定?

public class MultipleLoggingService : LoggingServiceBase 
{ 
    private readonly List<LoggingServiceBase> loggingServices; 

    public MultipleLoggingService(List<LoggingServiceBase> loggingServices) 
    { 
     this.loggingServices = loggingServices; 
     LogLevelChanged += OnLogLevelChanged; 
    } 

    private void OnLogLevelChanged(object sender, LogLevelChangedArgs args) 
    { 
     loggingServices.ForEach(l => l.LogLevel = LogLevel); 
    } 

    public override LogMessageResponse LogMessage(LogMessageRequest request) 
    { 
     if (request.LogMessage) 
      Parallel.ForEach(loggingServices, l => l.LogMessage(request)); 

     return new LogMessageResponse{MessageLogged = request.LogMessage}; 
    } 
} 

通知的LogMessage方法調用其他一些日誌服務:

這個問題在日誌類實現這個時候走了過來。我需要這部分立即返回,所以它不會阻止調用線程。


更新:基於他人的評論(我們已確認其行爲是#1)。所以,我已建議使用Task庫和重寫這樣的循環:

  if (request.LogMessage) 
      foreach (var loggingService in loggingServices) 
       Task.Factory.StartNew(() => loggingService.LogMessage(request)); 

回答

48

1號是正確的;直到循環完成,Parallel.ForEach纔會返回。如果你不想要這種行爲,你可以簡單地執行你的循環作爲Task並在另一個線程上運行它。

+0

謝謝你的提示!你碰巧在這個上下文中有一段關於如何使用「任務」的代碼片段?謝謝,Paul – 2012-04-14 12:57:08

+1

@PaulFryer:'Task t = Task.TaskFactory.StartNew(()=> {/ * Parallel.For去這裏* /});' – Richard 2012-04-14 13:50:34

+6

從技術上說,調用線程用於並行循環。所以它不會被「浪費」在循環中阻塞 - 它被用作工作線程之一。 – 2012-06-29 14:10:53

8

回覆您的更新,StartNew在正常的foreach():

這可能不是最優化的大集合,你沒有得到一個點來處理錯誤。

您的loggingServices可能不包含數千個項目,但錯誤處理仍然是一個問題。

考慮:

Task.Factory.StartNew(() => 
{ 
    try 
    { 
     Parallel.ForEach(loggingServices, l => l.LogMessage(request)); 
    } 
    catch(SomeException ex) 
    { 
     // at least try to log it ... 
    } 
}); 
+0

啊,好點。如果日誌記錄服務失敗,我將不得不考慮這一點,如何記錄錯誤:-) – 2012-04-14 14:02:54