Блокирует ли Parallel.ForEach?
Функция.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). Поэтому я принял совет использовать библиотеку задач и переписать цикл следующим образом:
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 ...
}
});