2012-04-14 31 views
39

Chức năng .net Parallel.ForEach có chặn chặn cuộc gọi không? Tôi đoán hành vi này là một trong những hành vi sau:Không Parallel.ForEach Block?

  1. Có, nó chặn cho đến khi mục chậm nhất thực hiện trả về.
  2. Không, nó không chặn và trả về kiểm soát ngay lập tức. Các mục để chạy song song được thực hiện trên các chủ đề nền.

Hoặc có thể có điều gì khác đang xảy ra, bất kỳ ai cũng biết chắc chắn?

Câu hỏi này đã đưa ra khi thực hiện điều này trong một lớp học khai thác gỗ:

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}; 
    } 
} 

Chú ý phương pháp LogMessage gọi một số dịch vụ khai thác gỗ khác. Tôi cần phần đó để trả về ngay lập tức, vì vậy nó không chặn chuỗi gọi.


Cập nhật: Dựa trên nhận xét của người khác (chúng tôi đã xác nhận hành vi là # 1). Vì vậy, tôi đã đưa ra lời khuyên để sử dụng thư viện Task và viết lại vòng lặp như sau:

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

Trả lời

48

Số 1 là chính xác; Parallel.ForEach không trở lại cho đến khi vòng lặp hoàn tất. Nếu bạn không muốn hành vi đó, bạn chỉ có thể thực hiện vòng lặp của mình dưới dạng Task và chạy nó trên một chuỗi khác.

+0

Thanks for the tip! Bạn có xảy ra một đoạn mã nhỏ về cách sử dụng 'Tác vụ' trong ngữ cảnh này không? Cảm ơn, Paul –

+1

@PaulFryer: 'Task t = Task.TaskFactory.StartNew (() => {/ * Parallel.For vào đây * /});' – Richard

+6

Về mặt kỹ thuật, chuỗi gọi được sử dụng trong vòng lặp song song. Vì vậy, nó không phải là "lãng phí" chỉ cần chặn trên vòng lặp - nó được sử dụng như một trong các chủ đề công nhân. –

8

Re cập nhật của bạn, StartNew trong một foreach bình thường():

Điều này có thể không phải là tối ưu nhất cho bộ sưu tập lớn, và bạn không nhận được một điểm để xử lý các lỗi.

Việc ghi nhật ký Dịch vụ của bạn có thể không chứa hàng nghìn mục nhưng việc xử lý lỗi vẫn là một điểm.

xem xét:

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

Ah, điểm tốt. Tôi sẽ phải suy nghĩ về điều này một chút, nếu dịch vụ đăng nhập thất bại, làm thế nào để đăng nhập lỗi :-) –