Блокирует ли Parallel.ForEach?

Функция.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));

2 ответа

Решение

Номер 1 правильный; Parallel.ForEach не возвращается, пока цикл не завершится. Если вы не хотите такого поведения, вы можете просто выполнить свой цикл как Task и запустить его в другом потоке.

Re ваше обновление, StartNew в нормальном foreach():

Это может быть не самым оптимальным для больших коллекций, и у вас нет смысла обрабатывать ошибки.

Ваши loggingServices, вероятно, не содержат тысячи элементов, но обработка ошибок остается точкой.

Рассматривать:

Task.Factory.StartNew(() => 
{
   try
   {
        Parallel.ForEach(loggingServices, l => l.LogMessage(request));
   }
   catch(SomeException ex)
   {
       // at least try to log it ...
   }
});
Другие вопросы по тегам