I am experiencing some oddities when using TaskFactory:
Task<int[]> parent = Task.Run(() =>
{
int length = 100;
var results = new int[length];
TaskFactory tf = new TaskFactory(TaskCreationOptions.AttachedToParent,
TaskContinuationOptions.ExecuteSynchronously);
// Create a list of tasks that we can wait for
List<Task> taskList = new List<Task>();
for (int i = 0; i < length - 1; i++) // have to set -1 on length else out of bounds exception, huh?
{
taskList.Add(tf.StartNew(() => results[i] = i));
}
// Now wait for all tasks to complete
Task.WaitAll(taskList.ToArray());
return results;
});
parent.Wait();
var finalTask = parent.ContinueWith(
parentTask =>
{
foreach (int i in parentTask.Result)
{
Console.WriteLine(i);
}
});
finalTask.Wait();
Console.ReadKey();
This gives an output similar to:
0 0 0 0 4 5 0 0 0 0 10 0 12 13 14 ... 0 99
I do not understand why not all indices are non zero.
Thanks,
Joe
When you capture a variable with a lambda, this variable is placed into a compiler-generated object which is shared between the inner and outer scope. When you do this:
The variable
iis shared between both your loop and all the child tasks. There is only oneiand it is being modified by the loop as the tasks are running. This is a race condition and will result in seemingly random data in the array every time, depending on how the tasks are scheduled.The simplest way to solve this is to make an immutable variable scoped to the loop body:
There is now a separate
innerIvariable for every task, and it is assigned the valueiexactly once and will not change.Imagine the old code as transformed by the compiler into
While the new code as transformed by the compiler becomes
Regarding why you had to use
length - 1: Some of your tasks didn't run until the loop was complete. At that point,i == length, so you would get anIndexOutOfRangeExceptionwhen trying to useias an index into your array. When you fix the race condition withi, this can no longer happen.