2016-04-27 14 views
0

Ich habe geschrieben für einfache Schleife durch Iteration durch Array und Parallel.ForEach Schleife das gleiche zu tun. Allerdings sind die Ergebnisse, die ich bekommen habe, unterschiedlich, also möchte ich fragen, was zum Teufel los ist? : DParallel.Foreach-Schleife erhält anderes Ergebnis als For-Schleife?

class Program 
    { 
    static void Main(string[] args) 
    { 
     long creating = 0; 
     long reading = 0; 
     long readingParallel = 0; 
     for (int j = 0; j < 10; j++) 
     { 
     Stopwatch timer1 = new Stopwatch(); 
     Random rnd = new Random(); 
     int[] array = new int[100000000]; 

     timer1.Start(); 
     for (int i = 0; i < 100000000; i++) 
     { 
      array[i] = rnd.Next(5); 
     } 
     timer1.Stop(); 

     long result = 0; 
     Stopwatch timer2 = new Stopwatch(); 

     timer2.Start(); 
     for (int i = 0; i < 100000000; i++) 
     { 
      result += array[i]; 
     } 
     timer2.Stop(); 

     Stopwatch timer3 = new Stopwatch(); 
     long result2 = 0; 
     timer3.Start(); 
     Parallel.ForEach(array, (item) => 
      { 
      result2 += item; 
      }); 

     if (result != result2) 
     { 
      Console.WriteLine(result + " - " + result2); 
     } 

     timer3.Stop(); 

     creating += timer1.ElapsedMilliseconds; 
     reading += timer2.ElapsedMilliseconds; 
     readingParallel += timer3.ElapsedMilliseconds; 
     } 



     Console.WriteLine("Create : \t" + creating/100); 
     Console.WriteLine("Read: \t\t" + reading/100); 
     Console.WriteLine("ReadP: \t\t" + readingParallel/100); 

     Console.ReadKey(); 
    } 
    } 

Also in dem Zustand ich Ergebnisse: result = 200009295; Ergebnis2 = 35163054;

Ist etwas falsch?

Antwort

4

Der += Operator ist nicht-atomarer und führt tatsächlich mehrere Operationen:

  • Lastwert an der Stelle, dass result zu zeigen, in dem Speicher
  • array[i] auf den In-Memory-Wert (ich bin Vereinfachung hier)
  • das Ergebnis zurückschreiben zu result

Da viele dieser op hinzufügen Es ist nicht nur möglich, aber es ist wahrscheinlich, dass es Rennen zwischen einigen dieser Operationen gibt, wo ein Thread einen result Wert liest und die Addition durchführt, aber bevor es die Möglichkeit hat, es zurückzuschreiben, greift ein anderer Thread der alte result Wert (der noch nicht aktualisiert wurde) und führt auch die Addition durch. Dann schreiben beide Threads ihre jeweiligen Werte in result. Unabhängig davon, wer das Rennen gewinnt, haben Sie eine geringere Anzahl als erwartet.

Dies ist der Grund, warum die Interlocked class existiert.

könnte Ihr Code sehr leicht behoben werden:

Parallel.ForEach(array, (item) => 
{ 
    Interlocked.Add(ref result2, item); 
}); 

nicht überrascht sein, wenn Sie Parallel.ForEach endet langsamer als die vollsynchrone Version in diesem Fall aber. Dies ist aufgrund der Tatsache, dass

  • die Menge der Arbeit innerhalb des Delegierten Sie Parallel.ForEach passieren sehr kleine
  • Interlocked Methoden ist eine leichte, aber nicht zu vernachlässigenden Aufwand entstehen, die in dieser deutlich spürbar sein wird, Sonderfall
+0

Vielen Dank für die ausführliche Erklärung. Natürlich hast du absolut recht! – Mastenka