Stuart Gordon
Stuart Gordon

Reputation: 185

Is creating tasks via a for loop different to creating them individually?

The problem i am having seems to be related to task creation. After using a loop to populate a task array, and starting them within a separate loop, my results, although consistent, are wrong. However, if i populate the array individually, the start each task within a loop, everything is fine. Can any one offer me some advice?

For example, this is problematic:

int c = 1;
for (int i = 1; i <= 4; i++)
    {
       taskArray[i-1] = new Task(() => calculateRows(c, true));
       c = c + 2;
    }
foreach (Task t in taskArray) t.Start();

but this works fine:

taskArray[0] = new Task(() => calculateRows(1, true));
taskArray[1] = new Task(() => calculateRows(3, true));
taskArray[2] = new Task(() => calculateRows(5, true));
taskArray[3] = new Task(() => calculateRows(7, true));
foreach (Task t in taskArray) t.Start();

Upvotes: 0

Views: 96

Answers (1)

Jon Skeet
Jon Skeet

Reputation: 1500485

The problem is that your lambda expression captures c - the variable c, not its value at the time of task creation. So by the time you start the tasks, c will be 9. Even if you started them within the loop, there's still no guarantee that the code in the lambda expression will start to execute before the change to c.

To fix this, you can use a separate local variable for each iteration of the loop:

int c = 1;
for (int i = 1; i <= 4; i++)
{
    int temp = c;
    taskArray[i-1] = new Task(() => calculateRows(temp, true));
    c = c + 2;
}
foreach (Task t in taskArray)
{
    t.Start();
}

Or bypass c entirely and compute temp from i:

for (int i = 1; i <= 4; i++)
{
    int temp = i * 2 - 1;
    taskArray[i-1] = new Task(() => calculateRows(temp, true));
}
foreach (Task t in taskArray)
{
    t.Start();
}

Do you need these all to be separate tasks though? Could you use Parallel.For instead? Or perhaps Enumerable.Range as suggested in comments:

var tasks = Enumerable.Range(1, 4)
                      .Select(x => new Task(() => calculateRows(x * 2 - 1, true)
                      .ToArray();
foreach (Task t in tasks)
{
    t.Start();
}

Upvotes: 6

Related Questions