Does Parallel.ForEach Block?

C#.NetMultithreadingLoggingParallel Processing

C# Problem Overview


Does the .net function Parallel.ForEach block the calling thread? My guess as to the behavior is one of these:

  1. Yes, it blocks until the slowest item executing returns.
  2. No, it doesn't block and returns control immediately. The items to run in parallel are done on background threads.

Or perhaps something else is happening, anyone know for sure?

This question came up when implementing this in a logging class:

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

Notice the LogMessage method calls some other logging services. I need that part to return immediately, so it doesn't block the calling thread.


Update: Based on comments from others (we have confirmed the behavior is #1). So I have taken advice to use the Task library and rewritten the loop like this:

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

C# Solutions


Solution 1 - C#

Number 1 is correct; Parallel.ForEach does not return until the loop has completed. If you don't want that behavior, you can simply execute your loop as a Task and run it on another thread.

Solution 2 - C#

Re your update, StartNew in a normal foreach() :

This may not be the most optimal for large collections, and you don't get a point to handle errors.

Your loggingServices probably doesn't hold thousands of items but the errorhandling remains a point .

Consider:

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionPaul FryerView Question on Stackoverflow
Solution 1 - C#GabeView Answer on Stackoverflow
Solution 2 - C#Henk HoltermanView Answer on Stackoverflow