Parallel.For(): Update variable outside of loop
Solution 1:
You can't do this. sum
is being shared across you parallel threads. You need to make sure that the sum
variable is only being accessed by one thread at a time:
// DON'T DO THIS!
Parallel.For(0, data.Count, i =>
{
Interlocked.Add(ref sum, data[i]);
});
BUT... This is an anti-pattern because you've effectively serialised the loop because each thread will lock on the Interlocked.Add
.
What you need to do is add sub totals and merge them at the end like this:
Parallel.For<int>(0, result.Count, () => 0, (i, loop, subtotal) =>
{
subtotal += result[i];
return subtotal;
},
(x) => Interlocked.Add(ref sum, x)
);
You can find further discussion of this on MSDN: http://msdn.microsoft.com/en-us/library/dd460703.aspx
PLUG: You can find more on this in Chapter 2 on A Guide to Parallel Programming
The following is also definitely worth a read...
Patterns for Parallel Programming: Understanding and Applying Parallel Patterns with the .NET Framework 4 - Stephen Toub
Solution 2:
sum += y;
is actually sum = sum + y;
. You are getting incorrect results because of the following race condition:
- Thread1 reads
sum
- Thread2 reads
sum
- Thread1 calculates
sum+y1
, and stores the result insum
- Thread2 calculates
sum+y2
, and stores the result insum
sum
is now equal to sum+y2
, instead of sum+y1+y2
.
Solution 3:
Your surmise is correct.
When you write sum += y
, the runtime does the following:
- Read the field onto the stack
- Add
y
to the stack - Write the result back to the field
If two threads read the field at the same time, the change made by the first thread will be overwritten by the second thread.
You need to use Interlocked.Add
, which performs the addition as a single atomic operation.
Solution 4:
Incrementing a long isn't an atomic operation.