Your problem is probably due to i++
not being thread-safe and your tasks being in a kind of 'race condition'.
Further explanation about i++ not being thread-safe you can find here: Are incrementers / decrementers (var++, var--) etc thread safe?
A quote of the answer given by Michael Burr in the aforementioned linked thread (upvote it there):
You can use something like InterlockedIncrement() depending on your
platform. On .NET you can use the Interlocked class methods
(Interlocked.Increment() for example).
A Rob Kennedy mentioned, even if the operation is implemented in terms
of a single INC instruction, as far as the memory is concerned a
read/increment/write set of steps is performed. There is the
opportunity on a multi-processor system for corruption.
There's also the volatile issue, which would be a necessary part of
making the operation thread-safe - however, marking the variable
volatile is not sufficient to make it thread-safe. Use the interlocked
support the platform provides.
This is true in general, and on x86/x64 platforms certainly.
The race to Trace.WriteLine()
Between the time you do ++i
and output i
, other parallel tasks might have changed/incremented i
several times.
Imagine your first task, which is incrementing i
so that it becomes 1. However, depending on your runtime environment and the weather of the day, i
might be incremented twenty times more by other parallel tasks before the first task outputs the variable -- which would now be 21 (and not 1 anymore). To prevent this from happening use a local variable which remembers the value of the incremented i
for a particular task for later processing/output:
int remember = Interlocked.Increment(ref i);
...
Trace.WriteLine("i of this task is: " + remember);