53

.net 函数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)。所以我接受了使用任务库的建议,并像这样重写了循环:

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

2 回答 2

68

1号是正确的;Parallel.ForEach在循环完成之前不会返回。如果您不想要这种行为,您可以简单地将循环作为 aTask执行并在另一个线程上运行它。

于 2012-04-14T12:47:30.080 回答
10

在正常的 foreach() 中重新更新 StartNew :

对于大型集合,这可能不是最佳的,而且您没有处理错误的意义。

您的 loggingServices 可能不包含数千个项目,但错误处理仍然很重要。

考虑:

Task.Factory.StartNew(() => 
{
   try
   {
        Parallel.ForEach(loggingServices, l => l.LogMessage(request));
   }
   catch(SomeException ex)
   {
       // at least try to log it ...
   }
});
于 2012-04-14T13:49:42.477 回答