2013-04-19 2 views
19

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

Так что мои вопросы:

  1. Почему Параллельно работал быстрее, чем задача?
  2. Имеют ли мои результаты, что я должен использовать Parallel вместо Task?
  3. Где я должен использовать Task и где Parallel?
  4. Какие преимущества использования Задачи по сравнению с Parallel?
  5. Задание - это просто обертка для метода 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

EDIT 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. Какие преимущества использования Задачи по сравнению с параллельными?

3. Есть ли задача просто обертка для метода ThreadPool.QueueUserWorkItem?

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

+2

tasks.Add (Task.Factory.StartNew (() => p.SomeLongOperation())); уже началось, но в этот момент ваш секундомер еще не начал подсчитывать! – David

+0

Как мне изменить код, чтобы сделать этот эксперимент более ясным? –

+0

Переместите его над startnew() – David

ответ

6

EDIT от this article из MSDN:

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

Похожие на вопросы:

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

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

+0

Мои результаты всегда почти одинаковы. Task.WaitAll работает медленнее, чем Parallel.Invoke. Возможно, я ничего не понимаю или такое сравнение неверно. В любом случае я хотел бы получить ответы на мои вопросы, чтобы прояснить эти подходы. Я думаю, что ответы на мои вопросы будут полезны и для других. –

+0

Только для информации: Сколько времени занимает 'SomeLongOperation()'? –

+1

, глядя на [this] (http://msdn.microsoft.com/en-us/library/ff963549.aspx) В статье MSDN говорится: «... Internally Parallel.Invoke создает новые задачи и ждет их. использует методы класса Task для этого. Вот пример ... » –

2

Если вы начинаете ненужные задачи (т. Е. «Void Задачи без возвращаемого значения») и сразу Wait для них, используйте вместо этого Parallel.Invoke. Ваше намерение сразу становится понятным читателю.

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

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

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


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

Task.Factory.StartNew(() => p.SomeLongOperation()); 

StartNew принимает Action, и выполняет это в новом главногоTask. Действие () => SomeLongOperation() создает подзадачу Task. После этой подзадачи создан (не завершен), звонок SomeLongOperation() возвращается, а Действие выполнено. Итак, mainTask уже завершен после десятой миллисекунды, в то время как две подзадачи у вас нет ссылки на , все еще работают в фоновом режиме. Параллельный путь также создает две подзадачи, которые он вообще не отслеживает и возвращает.

Правильный способ: tasks[0] = p.SomeLongOperation();, который присваивает выполняемую задачу массиву. Затем WaitAll проверяет завершение этой задачи.

Смежные вопросы