I was reading a book which show below example:
for (int i = 0; i < 10; i++)
{
Task.Factory.StartNew(() => Console.WriteLine(i));
}
Console.ReadLine();
below is the quote from the author:
The obvious intent of the code is to print out all the numbers from 0 to 9. They won’t necessarily be in order, because you are simply issuing work to the thread pool infrastructure, and thus you have no control over the order in which the tasks will run. But if you run the code, you will most likely have seen ten 10s on the screen The cause lies with the closure; the compiler will have had to capture local variable i and place it into a compiler-generated object on the heap, so that it can be referenced inside each of the lambdas. The question is, when does it create this object? As the local variable i is declared outside the loop body, the capture point is, therefore, also outside the loop body. This results in a single object being created to hold the value of i, and this single object is used to store each increment of i. Because each task will share the same closure object, by the time the first task runs the main thread will have completed the loop, and hence i is now 10. Therefore, all 10 tasks that have been created will print out the same value of i, namely 10.
I don't quite understand the part of " the compiler will have had to capture local variable i and place it into a compiler-generated object on the heap", isn't that i
is an integer type which is a value type that resides on the stack, how can we create an object on the heap so that this object contains a memery reference to the local i
varaible on the stack?
the author provides a fix as:
for (int i = 0; i < 10; i++)
{
int toCaptureI = i;
Task.Factory.StartNew(() => Console.WriteLine(toCaptureI));
}
Console.ReadLine();
which I still don't understand, if a new object is create on the heap to hold the reference of toCaptureI
, isn't that toCaptureI
will be assigned with a new value each iteration in the stack, then isn't it the same the faulty code above?