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

Метод Task.WaitAll vs Parallel.Invoke

У меня есть пример кода для сравнения времени обработки для параллельного подхода и подхода Task. Целью этого эксперимента является понимание того, как они работают.

Итак, мои вопросы:

  • Почему Parallel работала быстрее, чем Task?
  • Имеют ли мои результаты, что я должен использовать Parallel вместо Task?
  • Где я должен использовать Task и где Parallel?
  • Какие преимущества использования Задачи по сравнению с Parallel?
  • Есть ли задача только для метода ThreadPool.QueueUserWorkItem?

        public Task SomeLongOperation()
        {
            return Task.Delay(3000);
        }
    
        static void Main(string[] args)
        {
            Program p = new Program();
            List<Task> tasks = new List<Task>();
    
            tasks.Add(Task.Factory.StartNew(() => p.SomeLongOperation()));
            tasks.Add(Task.Factory.StartNew(() => p.SomeLongOperation()));
    
            var arr = tasks.ToArray();
    
            Stopwatch sw = Stopwatch.StartNew();
            Task.WaitAll(arr);
            Console.WriteLine("Task wait all results: " + sw.Elapsed);
            sw.Stop();
    
            sw = Stopwatch.StartNew();
            Parallel.Invoke(() => p.SomeLongOperation(), () => p.SomeLongOperation());
            Console.WriteLine("Parallel invoke results: " + sw.Elapsed);
            sw.Stop();
    
            Console.ReadKey();
        }
    

Вот мои результаты обработки: results

EDIT:

Измененный код выглядит следующим образом:

    Program p = new Program();
    Task[] tasks = new Task[2];

    Stopwatch sw = Stopwatch.StartNew();
    tasks[0] = Task.Factory.StartNew(() => p.SomeLongOperation());
    tasks[1] = Task.Factory.StartNew(() => p.SomeLongOperation());

    Task.WaitAll(tasks);
    Console.WriteLine("Task wait all results: " + sw.Elapsed);
    sw.Stop();

    sw = Stopwatch.StartNew();
    Parallel.Invoke(() => p.SomeLongOperation(), () => p.SomeLongOperation());
    Console.WriteLine("Parallel invoke results: " + sw.Elapsed);
    sw.Stop();

Мои новые результаты:

new results

ИЗМЕНИТЬ 2: Когда я заменил код Parallel.Invoke, чтобы быть первым, и Task.WaitAll будет второй, ситуация кардинально изменилась. Теперь Parallel работает медленнее. Это заставляет меня думать о некорректности моих оценок. Я изменил код, чтобы выглядеть так:

Program p = new Program();
Task[] tasks = new Task[2];

Stopwatch sw = null;
for (int i = 0; i < 10; i++)
{
    sw = Stopwatch.StartNew();
    Parallel.Invoke(() => p.SomeLongOperation(), () => p.SomeLongOperation());
    string res = sw.Elapsed.ToString();
    Console.WriteLine("Parallel invoke results: " + res);
    sw.Stop();
}

for (int i = 0; i < 10; i++)
{
    sw = Stopwatch.StartNew();
    tasks[0] = Task.Factory.StartNew(() => p.SomeLongOperation());
    tasks[1] = Task.Factory.StartNew(() => p.SomeLongOperation());
    Task.WaitAll(tasks);
    string res2 = sw.Elapsed.ToString();
    Console.WriteLine("Task wait all results: " + res2);
    sw.Stop();
}

И вот мои новые результаты:

enter image description here

enter image description here

Теперь я могу предположить, что этот эксперимент намного яснее. Результаты почти одинаковы. Иногда Параллельная, а иногда и задача быстрее. Теперь мои вопросы:

1. Где я должен использовать Task и где Parallel?

2. Какие преимущества использования Задачи по сравнению с Parallel?

3. Задача - это просто обертка для метода ThreadPool.QueueUserWorkItem?

Любая полезная информация, которая может прояснить эти вопросы, приветствуется.

4b9b3361

Ответ 1

EDIT от эта статья от MSDN

Обе параллели и задачи являются оболочками для ThreadPool. Параллельный вызов также ожидает, пока все задачи не будут завершены.

Относительно ваших вопросов:

Использование Task, Parallel или ThreadPool зависит от гранулярности управления, которое необходимо выполнить при выполнении ваших параллельных задач. Я лично привык к Task.Factory.StartNew(), но это личное мнение. То же самое относится к ThreadPool.QueueUserWorkItem()

Дополнительная информация: Первый вызов Parallel.Invoke() и Task.Factory.StartNew() может быть медленнее из-за внутренней инициализации.

Ответ 2

Если вы начинаете несрочные задачи и сразу Wait для них, используйте Parallel.Invoke. Ваше намерение сразу становится понятным читателю.

Используйте Задачи, если:

  • вы не ждете сразу
  • вам нужны возвращаемые значения
  • вам нужно указать параметры для методов, называемых
  • вам требуется TaskCreationOptions функциональность
  • вам нужны функции CancellationToken или TaskScheduler и не хотят использовать ParallelOptions

Да, вы можете обойти некоторые из них, например. Parallel.Invoke(() => p.OpWithToken(CancellationToken), но это запутывает ваши намерения. Parallel.Invoke предназначен для выполнения кучи работы, используя как можно больше мощности процессора. Это делается, он не заторможен, и вы знаете это заранее.