2013-04-19 2 views
19

Ich habe Beispielcode zum Vergleich der Verarbeitungszeit für Parallel-Ansatz und Aufgabe Ansatz. Das Ziel dieses Experiments ist das Verständnis, wie sie funktionieren.Task.WaitAll-Methode vs Parallel.Invoke-Methode

Also meine Fragen sind:

  1. Warum dann Task Parallel arbeitete schneller?
  2. Bedeutet mein Ergebnis, dass ich Parallel statt Task verwenden soll?
  3. Wo soll ich Task und wo Parallel?
  4. Welche Vorteile der Verwendung von Task im Vergleich zu Parallel?
  5. Ist Aufgabe nur ein Umbruch für die ThreadPool.QueueUserWorkItem-Methode?

    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(); 
        } 
    

Hier sind meine Verarbeitungsergebnisse: results

EDIT:

Geänderte Code wie folgt aussehen:

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(); 

Meine neue Ergebnisse:

new results

EDIT 2: Wenn ich Code mit Parallel.Invoke ersetzt ersten und Task.WaitAll sein zu Sekunde die Situation kardinal geändert wurde. Jetzt ist Parallel langsamer. Es lässt mich an die Unrichtigkeit meiner Schätzungen denken. Ich änderte Code wie folgt aussehen:

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(); 
} 

Und hier meine neuen Ergebnisse sind:

enter image description here

enter image description here

Jetzt kann ich vorschlagen, dass dieses Experiment sehr viel klarer ist. Die Ergebnisse sind fast gleich. Manchmal ist Parallel und manchmal Task schneller. Jetzt sind meine Fragen:

1. Wo soll ich Aufgabe verwenden und wo Parallel?

2. Welche Vorteile der Verwendung von Task im Vergleich zu Parallel?

3. Ist Aufgabe nur ein Umbruch für die ThreadPool.QueueUserWorkItem-Methode?

Alle hilfreiche Informationen, die diese Fragen klären können, sind willkommen.

+2

tasks.Add (Task.Factory.StartNew (() => p.SomeLongOperation())); schon gestartet, aber zu diesem Zeitpunkt hat deine Stoppuhr noch nicht angefangen zu zählen! – David

+0

Wie sollte ich den Code ändern, um dieses Experiment zu verdeutlichen? –

+0

Verschieben Sie es über die startnew() – David

Antwort

6

EDIT ab this article von MSDN:

sowohl parallel als auch Aufgabe sind Wrapper für Threadpool. Der parallele Aufruf wartet ebenfalls, bis alle Aufgaben beendet sind.

in Bezug auf Ihre Fragen:

mit dem Task, Parallel oder Threadpool ist abhängig von der Granularität der Kontrolle, die Sie für die Ausführung Ihrer parallelen Aufgaben haben müssen. Ich bin persönlich an Task.Factory.StartNew() gewöhnt, aber das ist eine persönliche Meinung. Gleiches gilt für ThreadPool.QueueUserWorkItem()

Weitere Informationen: Der erste Aufruf von Parallel.Invoke() und Task.Factory.StartNew() kann aufgrund interner Initialisierung langsamer sein.

+0

Meine Ergebnisse sind immer fast gleich. Task.WaitAll ist langsamer als Parallel.Invoice in irgendeiner Weise. Vielleicht verstehe ich etwas nicht oder ein solcher Vergleich ist inkorrekt. Irgendwie möchte ich Antworten auf meine Fragen bekommen, um diese Ansätze zu klären. Ich denke, Antworten auf meine Fragen werden auch für andere nützlich sein. –

+0

Nur zur Information: Wie viel Zeit braucht 'SomeLongOperation()'? –

+1

Blick auf [dies] (http://msdn.microsoft.com/en-us/library/ff963549.aspx) MSDN Artikel sagt es: "... Intern erstellt Parallel.Invoke neue Aufgaben und wartet auf sie. Es verwendet dazu Methoden der Task-Klasse, hier ein Beispiel ... " –

2

Wenn Sie nicht generische Tasks (d. H. "Void Tasks ohne Rückgabewert") und sofort Wait für sie starten, verwenden Sie stattdessen Parallel.Invoke. Ihre Absicht ist dem Leser sofort klar.

verwenden, um Aufgaben, wenn:

  • Sie Warten Sie nicht sofort
  • müssen Sie Werte zurück
  • Sie Parameter an die Methoden
  • Sie benötigen TaskCreationOptions Funktionalität
  • Sie benötigen genannt geben müssen CancellationToken oder TaskScheduler Funktionalität und wollen nicht ParallelOptions
  • verwenden
  • im Grunde, wenn Sie mehr Optionen oder Kontrolle wünschen

Ja, Sie können einige dieser, z. Parallel.Invoke(() => p.OpWithToken(CancellationToken) aber das verschleiert deine Absicht. Parallel.Invoke ist für einen Haufen Arbeit mit so viel CPU-Leistung wie möglich. Es wird getan, es blockiert nicht, und Sie wissen das im Voraus.


Ihre Tests sind jedoch schrecklich. Die rote Flagge würde bedeuten, dass Ihre lange Aktion 3000 Millisekunden warten soll, aber Ihre Tests dauern weniger als ein Zehntel einer Millisekunde.

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

StartNew nimmt eine Action, und führt diese in einer neuen Haupt Task. Die Aktion () => SomeLongOperation() erstellt eine TeilaufgabeTask. Nachdem diese Unteraufgabe (nicht abgeschlossen) erstellt wurde, wird der Aufruf an SomeLongOperation() zurückgegeben, und die Aktion ist fertig. So ist die HauptTask bereits nach einer zehnten Millisekunde abgeschlossen, während die beiden Subtasks, auf die Sie keinen Bezug haben , immer noch im Hintergrund laufen. Der parallele Pfad erstellt auch zwei Teilaufgaben, die er überhaupt nicht verfolgt und zurückgibt.

Der richtige Weg wäre tasks[0] = p.SomeLongOperation();, der dem Array eine laufende Aufgabe zuweist. Dann prüft WaitAll auf den Abschluss dieser Aufgabe.