0

I have a for loop to create a number of Tasks that are perameterised:

int count = 16;

List<Tuple<ulong, ulong>> brackets = GetBrackets(0L, (ulong)int.MaxValue, count);
Task[] tasks = new Task[count];

s.Reset();
s.Start();

for(int i = 0; i < count; i++)
{
    tasks[i] = Task.Run(() => TestLoop(brackets[i].Item1, brackets[i].Item2));
}

Task.WaitAll(tasks);

s.Stop();

times.Add(count, s.Elapsed);

However, when this runs, an exception is thrown by the line inside the For loop, that brackets[i] does not exist, because i at that point is 16, even though the loop is set to run while i < count.

If I change the line to this:

tasks[i] = new Task(() => TestLoop(brackets[0].Item1, brackets[0].Item2));

Then no error is thrown. Also, if I walk through the loop with breakpoints, no issue is thrown.

For repro, I also include GetBrackets, which just breaks a number range into blocks:

private List<Tuple<ulong, ulong>> GetBrackets(ulong start, ulong end, int threads)
{
    ulong all = (end - start);
    ulong block = (ulong)(all / (ulong)threads);
    List<Tuple<ulong, ulong>> brackets = new System.Collections.Generic.List<Tuple<ulong, ulong>>();

    ulong last = 0;
    for (int i=0; i < threads; i++)
    {
        brackets.Add(new Tuple<ulong, ulong>(last, (last + block - 1)));
        last += block;
    }

    // Hack
    brackets[brackets.Count - 1] = new Tuple<ulong, ulong>(
        brackets[brackets.Count - 1].Item1, end);

    return brackets;
}

Could anyone shed some light on this?

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194

1 Answers1

0

(This is a duplicate of similar posts, but they're often quite hard to find and the symptoms often differ slightly.)

The problem is that you're capturing the variable i in your loop:

for(int i = 0; i < count; i++)
{
    tasks[i] = Task.Run(() => TestLoop(brackets[i].Item1, brackets[i].Item2));
}

You've got a single i variable, and the lambda expression captures it - so by the time your task actually starts executing the code in the lambda expression, it probably won't have the same value as it did before. You need to introduce a separate variable inside the loop, so that each iteration captures a different variable:

for (int i = 0; i < count; i++)
{
    int index = i;
    tasks[i] = Task.Run(() => TestLoop(brackets[index].Item1, brackets[index].Item2));
}

Alternatively, use LINQ to create the task array:

var tasks = brackets.Select(t => Task.Run(() => TestLoop(t.Item1, t.Item2));
                    .ToArray(); // Or ToList
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194