Parallel.ForEach Block?

Pau*_*yer 47 .net c# parallel-processing logging multithreading

.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};
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,该LogMessage方法调用其他一些日志服务.我需要该部分立即返回,因此它不会阻止调用线程.


更新:根据其他人的评论(我们已确认行为是#1).所以我已经建议使用Task库并重写循环,如下所示:

          if (request.LogMessage)
            foreach (var loggingService in loggingServices)
                Task.Factory.StartNew(() => loggingService.LogMessage(request));
Run Code Online (Sandbox Code Playgroud)

Gab*_*abe 57

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

  • 从技术上讲,调用线程用于并行循环.所以它不会"浪费"只是阻塞循环 - 它被用作工作线程之一. (8认同)
  • @PaulFryer:`任务t = Task.TaskFactory.StartNew(()=> {/*Parallel.For go here*/});` (3认同)

Hen*_*man 10

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

对于大型集合而言,这可能不是最佳选择,并且您无法处理错误.

您的loggingServices可能不包含数千个项目,但错误处理仍然是一个问题.

考虑:

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