Подтвердить что ты не робот

Есть ли Parallel.ForEach Block?

Поддерживает ли функция .net Parallel.ForEach блокировать вызывающий поток? Мое предположение о поведении является одним из следующих:

  • Да, он блокируется до тех пор, пока не вернется самый медленный результат выполнения.
  • Нет, он не блокирует и немедленно возвращает управление. Элементы для параллельной работы выполняются по фоновым потокам.

Или, возможно, что-то еще происходит, кто-нибудь знает наверняка?

Этот вопрос возник при реализации этого в классе ведения журнала:

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). Поэтому я посоветовался использовать библиотеку Task и переписал цикл следующим образом:

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

Ответ 1

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

Ответ 2

Повторите свое обновление, StartNew в обычном foreach():

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

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

Рассмотрим:

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