1

I am doing async programming with C# and I pass the loop counter 'i' as a parameter inside the Run method of Task to run it it asynchronously. Everything works fine except that the loop counter 'i' inside each task is updated whenever it is incremented in the caller. In other words, the 'i' does not keep its value and I am not sure how to fix that.

//A queue for all the tasks
var tasks = new BlockingCollection<Task<ResultWrapper>>();
for (int i = 1; i <= 10; i++)
{   
    Console.WriteLine("frame: " + i);
    //Grab a frame
    Bitmap frame = reader.ReadVideoFrame(i);

    //Add the frames to the queue and run them asynchronously
    tasks.Add(Task.Run(() => RequestAnalysis(projectId, endpoint, frame, i))); 
}

And here is the code for RequestAnalysis() function:

private static ResultWrapper RequestAnalysis(Guid projectId, PredictionEndpoint endpoint, Bitmap frame, int i)
{
    Console.WriteLine("Frame {0} task is running", i);
    //some other stuff here...
}

Here is a sample output:

frame: 1
frame: 2
frame: 3
frame: 4
frame: 5
Frame 3 task is running
Frame 3 task is running
frame: 6
Frame 4 task is running
frame: 7
frame: 8
frame: 9
Frame 5 task is running
frame: 10
Frame 11 task is running
Frame 11 task is running
Frame 11 task is running
Frame 11 task is running
Frame 11 task is running
Frame 11 task is running

But I expect it to be something like this:

frame: 1
frame: 2
frame: 3
frame: 4
frame: 5
Frame 1 task is running
Frame 2 task is running
frame: 6
Frame 3 task is running
frame: 7
frame: 8
frame: 9
Frame 4 task is running
frame: 10
Frame 5 task is running
Frame 6 task is running
Frame 7 task is running
Frame 8 task is running
Frame 9 task is running
Frame 10 task is running

I would appreciate any help. I am new to C# and I am not sure what I am missing.

Thanks.

S.Kr
  • 25
  • 1
  • 5
  • The lambda expression you use for the task captures the variable `i`, not the current value of `i`. Since the tasks start running while (or after) the for loop executes, the tasks see whatever value `i` has at that moment in/after the for-loop. –  Jul 06 '18 at 20:33
  • Solution: Declare a local variable `var temp = i;` right before `Task.Add(...)` and use `temp` as argument in your lambda expression. (Since `temp` -- being declared inside the body of the for-loop -- will be recreated for every single for-loop iteration, every task you create/run will capture and see its own separate `temp`) –  Jul 06 '18 at 20:35

1 Answers1

0

Bind the lambda to a variable that doesn't change. For example:

int currentIndex = i;
tasks.Add(Task.Run(() => RequestAnalysis(projectId, endpoint, frame, currentIndex)));
jspcal
  • 50,847
  • 7
  • 72
  • 76