ArgumentOutOfRangeException. But it should not be there

So I have and method like this.

var someColletion = _someService.GetSomeCollection(someParam);

var taskCollection = new Task<double>[someCollection.Count];

for (int i = 0; i < taskCollection.Length; i++)
{
    // do some stuff on the i-th element of someCollection and taskCollection 
    // and start the i-th task
}

Task.WaitAll(taskCollection);

double total = 0;

for (int i = 0; i < taskCollection.Length; i++)
{
    // get the result of each task and sum it in total variable
}

return total;

the case is when it comes into first for loop and the number of elements in both collections are suppose 1 the ArgumentOutOfRangeException is being thrown and then AggregateException is being thrown on Task.WaitAll() because the i becomes 1 (I don't know why but it does) and when it tries to access the i-th (second) element in array that contains just one element, this happens. But there is more to this. If i set a break point before first loop and go step by step then this thing does not happen. when i becomes one the cycle ends. and everything's okay. now the method I provided above is called by an ASP.NET MVC Controller's Action which itself is called Asynchronously (by ajax call) suppose 3 times. and out of this three just one executes correctly other two do the thing I said above (if not breakpointed). I think that this problem is caused by ajax call most probably because when I breakpoint it stops other calls from executing. Can anyone suggest anything ?

Jon Skeet
people
quotationmark

I suspect you're using i within the first loop, capturing it with a lambda expression or anonymous method, like this:

for (int i = 0; i < taskCollection.Length; i++)
{
    taskCollection[i] = Task.Run(() => Console.WriteLine(i));
}

If that's the case, it's the variable i which is being captured - not the value of the variable for that iteration of the loop. So by the time the task actually executes, it's likely that the value of i has changed. The solution is to take a copy of the iteration variable within the loop, in a separate "new" variable, and capture that in the anonymous function instead:

for (int i = 0; i < taskCollection.Length; i++)
{
    int copy = i;
    taskCollection[i] = Task.Run(() => Console.WriteLine(copy));
}

That way, each task captures a separate variable, whose value never changes.

people

See more on this question at Stackoverflow