Wie die anderen Antworten erwähnt, ist das Aktualisieren der sum
Variable aus mehreren Threads (was Parallel.ForEach tut) keine Thread-sichere Operation. Die triviale Behebung des Erlangens einer Sperre vor der Aktualisierung behebt das Problem.
double sum = 0.0;
Parallel.ForEach(myCollection, arg =>
{
lock (myCollection)
{
sum += ComplicatedFunction(arg);
}
});
Allerdings führt das ein weiteres Problem ein. Da die Sperre bei jeder Iteration akquiriert wird, bedeutet dies, dass die Ausführung jeder Iteration effektiv serialisiert wird. Mit anderen Worten, es wäre besser gewesen, einfach einen einfachen alten foreach
Loop zu verwenden.
Jetzt ist der Trick, dies richtig zu machen, das Problem in separate und unabhängige Chucks zu teilen. Glücklicherweise ist das sehr einfach zu tun, wenn Sie nur das Ergebnis der Iterationen summieren wollen, weil die Summenoperation kommutativ und assoziativ ist und weil die Zwischenergebnisse der Iterationen unabhängig sind.
Also hier ist, wie Sie es tun.
double sum = 0.0;
Parallel.ForEach(myCollection,
() => // Initializer
{
return 0D;
},
(item, state, subtotal) => // Loop body
{
return subtotal += ComplicatedFunction(item);
},
(subtotal) => // Accumulator
{
lock (myCollection)
{
sum += subtotal;
}
});
See [ Schritt ein Zählwert außerhalb Parallel.ForEach Umfang ] (http://stackoverflow.com/questions/2394447/increment-a-count-value-outside-parallel-foreach-scope). Grundsätzlich können Sie [Interlocked] (http://msdn.microsoft.com/en-us/library/55dzx06b.aspx) verwenden, wenn es nötig ist, aber es ist besser, wenn möglich Nebenwirkungen zu vermeiden. –