No, it is not defined behaviour. It is a race condition, where each output may be any value from 0 to 10, in any order. It depends on the timing of the execution of the generated tasks with respect to the main program; this, in turn, largely depends on the multiprocessing capabilities of your machine. If a task happens to be picked up by a worker thread and executed before the loop was completed, then you would get whatever value happened to be in the loop counter at that point in time.
The reason that you often get just the final value of the loop counter is that the main loop is computationally trivial, and completes before any of the tasks get a chance to run. If you want to observe the non-determinism, add an artificial delay to the loop:
for (int i = 0; i < 10; i++)
{
Task.Factory.StartNew(() => Console.WriteLine("This is task " + i));
Thread.SpinWait(20000);
}
Sample output on an Intel Core i7:
This is task 2
This is task 3
This is task 3
This is task 3
This is task 4
This is task 5
This is task 7
This is task 8
This is task 9
This is task 8
If you use a sufficiently-large delay, the tasks would get the chance to run in the intuitively-expected order, outputting 0, 1, 2, ..., 9
.