2017-12-03 11 views
0

Meine Aufgabe ist es, Zahlen in einem Bereich zusammenzufassen, um zu erreichen, dass ich Threads verwenden muss, um die Berechnung zu trennen. Ich habe die Nummer in Teile aufgeteilt und für jedes Teil einen Thread verwendet.Thread Summierung Berechnung Fehler

public class ParallelCalc 
{ 
    public long resultLong; 
    private Thread[] threads; 
    private List<long> list = new List<long>(); 

    public long MaxNumber { get; set; } 
    public int ThreadsNumber { get; set; } 

    public event CalcFinishedEventHandler finished; 

    public ParallelCalc(long MaxNumber, int ThreadsNumber) 
    { 
     this.MaxNumber = MaxNumber; 
     this.ThreadsNumber = ThreadsNumber; 
     this.threads = new Thread[ThreadsNumber]; 
    } 

    public void Start() 
    { 
     Stopwatch sw = new Stopwatch(); 

     for (int i = 0; i < ThreadsNumber; i++) 
     { 

      threads[i] = new Thread(() => Sum(((MaxNumber/ThreadsNumber) * i) + 1, 
       MaxNumber/ThreadsNumber * (i + 1))); 

      if (i == ThreadsNumber - 1) 
      { 
       threads[i] = new Thread(() => Sum(((MaxNumber/ThreadsNumber) * i) + 1, 
            MaxNumber)); 
      } 

      sw.Start(); 
      threads[i].Start(); 
     } 

     while (threads.All(t => t.IsAlive)); 
     sw.Stop(); 

     finished?.Invoke(this, 
      new CalcFinishedEventArgs() 
      { 
       Result = list.Sum(), 
       Time = sw.ElapsedMilliseconds 
      }); 
    } 


    private void Sum(long startNumber, long endnumber) 
    { 
     long result = 0; 

     for (long i = startNumber; i <= endnumber; i++) 
     { 
      result += i; 
     } 

     list.Add(result); 

    } 

} 

Das Ergebnis muss die Summe der Zahlen sein, jedoch ist es aufgrund der asynchronen Threadzuweisung in der Liste falsch. Bitte geben Sie den Fehler an.

+3

Sie verwenden viele gemeinsam genutzte Variablen, von denen die meisten Typen als unsicher für den Multithread-Zugriff gelten. Warum erwarten Sie, dass wir Ihre Probleme diagnostizieren, wenn Sie so wenig Mühe haben, sie selbst zu korrigieren? –

+0

Ich frage nur, was in diesem Fall zu verwenden ist, um korrekte Ergebnisse von mehreren Threads zu erhalten. –

+0

Sie sollten eine [Parallel.For Loop] verwenden (https://docs.microsoft.com/en-us/dotnet/standard/parallel-programming/how-to-write-a-simple-parallel-for-loop) stattdessen. –

Antwort

3

Es gibt mehr als eine Sache falsch ist hier, halten Sie sich fest ...

  • Start ein Stopwatch sw erstellt, aber Sie rufen sw.Start bei jeder Iteration der Schleife. Starten Sie es nur einmal.

  • Wenn i == ThreadsNumber - 1 zu true auswertet, lassen Sie Thread zu Müll. Ich verstehe nicht warum ...

    (MaxNumber/ThreadsNumber) * (i + 1) WHEN i == ThreadsNumber - 1 
    = 
    (MaxNumber/ThreadsNumber) * (ThreadsNumber - 1 + 1) 
    = 
    (MaxNumber/ThreadsNumber) * (ThreadsNumber) 
    = 
    MaxNumber 
    

    Haben Sie Rundungsprobleme? Wie folgt umschreiben:

    ((i + 1) * MaxNumber)/ThreadsNumber 
    

    Durch die letzte Division vermeiden Sie das Rundungsproblem.

  • Sie sind Spin warten auf die Threads while (threads.All(t => t.IsAlive));. Sie könnten auch Thread.Join oder besser noch verwenden, lassen Sie die Threads Sie benachrichtigen, wenn sie fertig sind.

  • Die Bereiche in den Lambdas haben einen Verschluss auf i. Sie müssen vorsichtig sein mit C# - For loop and the lambda expressions.

  • List<T> ist nicht threadsicher. Ich würde vorschlagen, ein einfaches Array zu verwenden (Sie wissen, die Anzahl der Threads nachher) und jedem Thread zu sagen, nur an der Position zu speichern, die ihnen entspricht.

  • Sie haben nicht berücksichtigt, was passieren würde, wenn ein zweiter Anruf an Start vor dem ersten erfolgt.


So werden wir ein Array für die Ausgabe haben:

var output = new long[ThreadsNumber]; 

und eine für die Themen:

var threads = new Thread[ThreadsNumber]; 

Hmm, fast wie wir schaffen sollten Klasse.

Wir werden die Stoppuhr haben:

var sw = new Stopwatch(); 

Lassen Sie uns es einmal starten:

sw.Start(); 

Jetzt ein for die Themen zu erstellen:

for (var i = 0; i < ThreadsNumber; i++) 
{ 
    // ... 
} 

Haben Sie eine Kopie i, um Probleme zu vermeiden:

for (var i = 0; i < ThreadsNumber; i++) 
{ 
    var index = i; 
    // ... 
} 

Compute der Bereich für den aktuellen Thread:

for (var i = 0; i < ThreadsNumber; i++) 
{ 
    var index = i; 
    var start = 1 + (i * MaxNumber)/ThreadsNumber; 
    var end = ((i + 1) * MaxNumber)/ThreadsNumber; 
    // ... 
} 

Wir brauchen Sum in einer solchen Art und Weise zu schreiben, dass wir die Ausgabe im Array speichern kann:

private void Sum(long startNumber, long endNumber, int index) 
{ 
    long result = 0; 
    for (long i = startNumber; i <= endnumber; i++) 
    { 
     result += i; 
    } 
    output[index] = result; 
} 

Hmm .. Warten Sie, es gibt einen besseren Weg ...

private static void Sum(long startNumber, long endNumber, out long output) 
{ 
    long result = 0; 
    for (long i = startNumber; i <= endNumber; i++) 
    { 
     result += i; 
    } 
    output = result; 
} 

Hmm ... nein, können wir es besser machen ...

private static long Sum(long startNumber, long endNumber) 
{ 
    long result = 0; 
    for (long i = startNumber; i <= endNumber; i++) 
    { 
     result += i; 
    } 
    return result; 
} 

Erstellen Sie die Thread

for (var i = 0; i < ThreadsNumber; i++) 
{ 
    var index = i; 
    var start = 1 + (i * MaxNumber)/ThreadsNumber; 
    var end = ((i + 1) * MaxNumber)/ThreadsNumber; 
    threads[i] = new Thread(() => output[index] = Sum(start, end)); 
    // ... 
} 

und starten Sie die Thread:

for (var i = 0; i < ThreadsNumber; i++) 
{ 
    var index = i; 
    var start = 1 + (i * MaxNumber)/ThreadsNumber; 
    var end = ((i + 1) * MaxNumber)/ThreadsNumber; 
    threads[i] = new Thread(() => {output[index] = Sum(start, end);}); 
    threads[i].Start(); 
} 

Sind wir wirklich auf diese warten?

denken, denken ...

Wir verfolgen, wie viele Threads warten auf ... und wenn sie alle fertig sind, rufen wir die Veranstaltung (und beenden die Stoppuhr).

var pendingThreads = ThreadsNumber; 

// ... 

for (var i = 0; i < ThreadsNumber; i++) 
{ 
    // ... 
    threads[i] = new Thread 
    (
     () => 
     { 
      output[index] = Sum(start, end); 
      if (Interlocked.Decrement(ref pendingThreads) == 0) 
      { 
       sw.Stop(); 
       finished?.Invoke 
       (
        this, 
        new CalcFinishedEventArgs() 
        { 
         Result = output.Sum(), 
         Time = sw.ElapsedMilliseconds 
        } 
       ); 
      } 
     } 
    ); 
    // ... 
} 

Lassen Sie uns es bringen alle togheter:

void Main() 
{ 
    var pc = new ParallelCalc(20, 5); 
    pc.Finished += (sender, args) => 
    { 
     Console.WriteLine(args); 
    }; 
    pc.Start(); 
} 

public class CalcFinishedEventArgs : EventArgs 
{ 
    public long Result {get; set;} 
    public long Time {get; set;} 
} 

public class ParallelCalc 
{ 
    public long MaxNumber { get; set; } 
    public int ThreadsNumber { get; set; } 

    public event EventHandler<CalcFinishedEventArgs> Finished; 

    public ParallelCalc(long MaxNumber, int ThreadsNumber) 
    { 
     this.MaxNumber = MaxNumber; 
     this.ThreadsNumber = ThreadsNumber; 
    } 

    public void Start() 
    { 
     var output = new long[ThreadsNumber]; 
     var threads = new Thread[ThreadsNumber]; 
     var pendingThreads = ThreadsNumber; 
     var sw = new Stopwatch(); 
     sw.Start(); 
     for (var i = 0; i < ThreadsNumber; i++) 
     { 
      var index = i; 
      var start = 1 + (i * MaxNumber)/ThreadsNumber; 
      var end = ((i + 1) * MaxNumber)/ThreadsNumber; 
      threads[i] = new Thread 
      (
       () => 
       { 
        output[index] = Sum(start, end); 
        if (Interlocked.Decrement(ref pendingThreads) == 0) 
        { 
         sw.Stop(); 
         Finished?.Invoke 
         (
          this, 
          new CalcFinishedEventArgs() 
          { 
           Result = output.Sum(), 
           Time = sw.ElapsedMilliseconds 
          } 
         ); 
        } 
       } 
      ); 
      threads[i].Start(); 
     } 
    } 

    private static long Sum(long startNumber, long endNumber) 
    { 
     long result = 0; 
     for (long i = startNumber; i <= endNumber; i++) 
     { 
      result += i; 
     } 
     return result; 
    } 
} 

Ausgang:

Result 
210 

Time 
0 

, die zu schnell ist ... lassen Sie mich Eingang:

var pc = new ParallelCalc(2000000000, 5); 
pc.Finished += (sender, args) => 
{ 
    Console.WriteLine(args); 
}; 
pc.Start(); 

Ausgang:

Result 
2000000001000000000 

Time 
773 

And that is correct.

Und ja, dieser Code kümmert sich um den Fall des Aufrufs Start mehrere Male. Beachten Sie, dass es jedes Mal ein neues Array für die Ausgabe und ein neues Array von Threads erstellt. Auf diese Weise stolpert es nicht über sich selbst.

Ich lasse Fehlerbehandlung an Sie. Hinweise: MaxNumber/ThreadsNumber -> Division durch 0, und (i + 1) * MaxNumber -> Überlauf, ganz zu schweigen output.Sum() -> Überlauf.