В чем разница между использованием Parallel.ForEach или Task.Run () для асинхронного запуска набора задач?
Версия 1:
List<string> strings = new List<string> { "s1", "s2", "s3" };
Parallel.ForEach(strings, s =>
{
DoSomething(s);
});
Версия 2:
List<string> strings = new List<string> { "s1", "s2", "s3" };
List<Task> Tasks = new List<Task>();
foreach (var s in strings)
{
Tasks.Add(Task.Run(() => DoSomething(s)));
}
await Task.WhenAll(Tasks);
c#
async-await
parallel.foreach
Петтер Т
источник
источник
Task.WaitAll
вместоTask.WhenAll
.Ответы:
В этом случае второй метод будет асинхронно ожидать завершения задач вместо блокировки.
Тем не менее, есть недостаток для использования
Task.Run
в циклеParallel.ForEach
- с,Partitioner
который создается, чтобы избежать выполнения большего количества задач, чем необходимо.Task.Run
всегда будет выполнять одну задачу для каждого элемента (поскольку вы это делаете), ноParallel
пакеты классов работают так, что вы создаете меньше задач, чем всего рабочих элементов. Это может обеспечить значительно лучшую общую производительность, особенно если тело цикла выполняет небольшое количество работы на элемент.Если это так, вы можете объединить оба варианта, написав:
Обратите внимание, что это также может быть записано в этой более короткой форме:
источник
DoSomething
естьasync void DoSomething
?async Task DoSomething
?Первая версия будет синхронно блокировать вызывающий поток (и запускать на нем некоторые задачи).
Если это поток пользовательского интерфейса, это замораживает пользовательский интерфейс.
Вторая версия будет запускать задачи асинхронно в пуле потоков и освобождать вызывающий поток, пока они не будут выполнены.
Существуют также различия в используемых алгоритмах планирования.
Обратите внимание, что ваш второй пример может быть сокращен до
источник
await Task.WhenAll(strings.Select(async s => await Task.Run(() => DoSomething(s)));
? У меня были проблемы при возврате задач (а не ожидании), особенно когдаusing
для удаления объектов использовались операторы типа like .Я закончил тем, что сделал это, поскольку это было легче читать:
источник
Я видел, что Parallel.ForEach использовался ненадлежащим образом, и я подумал, что пример в этом вопросе поможет.
Когда вы запустите приведенный ниже код в консольном приложении, вы увидите, как задачи, выполняемые в Parallel.ForEach, не блокируют вызывающий поток. Это может быть хорошо, если вы не заботитесь о результате (положительном или отрицательном), но если вам нужен результат, вы должны обязательно использовать Task.WhenAll.
Вот результат:
Вывод:
Использование Parallel.ForEach с задачей не блокирует вызывающий поток. Если вы заботитесь о результате, обязательно дождитесь выполнения заданий.
~ Приветствия
источник