it-swarm.com.ru

Parallel.ForEach vs Task.Run и Task.WhenAll

В чем разница между использованием 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);
118
Petter T

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

Тем не менее, есть недостаток в использовании Task.Run в цикле - с Parallel.ForEach существует Partitioner , который создается, чтобы избежать выполнения большего количества задач, чем необходимо. Task.Run всегда выполняет одну задачу для каждого элемента (поскольку вы это делаете), но классы Parallel работают так, что вы создаете меньше задач, чем общее количество рабочих элементов. Это может обеспечить значительно лучшую общую производительность, особенно если тело цикла выполняет небольшое количество работы на элемент.

Если это так, вы можете объединить оба варианта, написав:

await Task.Run(() => Parallel.ForEach(strings, s =>
{
    DoSomething(s);
}));

Обратите внимание, что это также может быть записано в этой более короткой форме:

await Task.Run(() => Parallel.ForEach(strings, DoSomething));
119
Reed Copsey

Первая версия будет синхронно блокировать вызывающий поток (и запускать некоторые задачи в нем).
Если это поток пользовательского интерфейса, это замораживает пользовательский интерфейс.

Вторая версия будет выполнять задачи асинхронно в пуле потоков и освобождать вызывающий поток, пока они не будут выполнены.

Существуют также различия в используемых алгоритмах планирования.

Обратите внимание, что ваш второй пример может быть сокращен до

await Task.WhenAll(strings.Select(s => Task.Run(() => DoSomething(s)));
30
SLaks

Я закончил тем, что сделал это, поскольку это было легче читать:

  List<Task> x = new List<Task>();
  foreach(var s in myCollectionOfObject)
  {
      // Note there is no await here. Just collection the Tasks
      x.Add(s.DoSomethingAsync());
  }
  await Task.WhenAll(x);
0
Chris M.