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.
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? –
Ich frage nur, was in diesem Fall zu verwenden ist, um korrekte Ergebnisse von mehreren Threads zu erhalten. –
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. –