Skip to main content

Closure and Captured variables

Here is a sample code. Can you guess what the output would be ?

 
public static class TasksExample { public static void Execute() { Task[] tasks = new Task[10]; for (int i = 0; i < 10; i++) { tasks[i] = Task.Run(() => CalculateResult(i)); } Task.WaitAll(tasks); //ask.Wait(); //Console.WriteLine(tasks[i].Result); Console.WriteLine("Next work..."); } public static int CalculateResult(int i) { Console.WriteLine($"work starting {i}"); Thread.Sleep(10000); Console.WriteLine($"Completed work {i}"); return 100; } }
Pat yourself if your answer was like this
work starting 10 work starting 10 work starting 10 work starting 10 work starting 10 work starting 10 work starting 10 work starting 10 work starting 10 work starting 10 Completed work 10 Completed work 10 Completed work 10 Completed work 10 Completed work 10 Completed work 10 Completed work 10 Completed work 10 Completed work 10 Completed work 10 Next work...
so why is this? For loop is iterating nicely but why did all the tasks got value 10? The answer lies in Closure and Captured variable. The scope of i is in for loop. When you pass the variable to an anonymous function, it wont use the value until the function is executed. In this case it is after the for loop has exited and now the value of i is 10. Hence when Task executes CalculateResult, all tasks now will have value 10. Awesome!!!

If you are using Resharper, it will give you a warning "Access to modified closure". I can't explain it better than here https://www.jetbrains.com/help/resharper/AccessToModifiedClosure.html

So the solution to above issue is to copy the value of i to a local variable with the loop as shown here

public static class TasksExample { public static void Execute() { Task[] tasks = new Task[10]; for (int i = 0; i < 10; i++) { var i1 = i; tasks[i] = Task.Run(() => CalculateResult(i1)); } Task.WaitAll(tasks); //ask.Wait(); //Console.WriteLine(tasks[i].Result); Console.WriteLine("Next work..."); } public static int CalculateResult(int i) { Console.WriteLine($"work starting {i}"); Thread.Sleep(10000); Console.WriteLine($"Completed work {i}"); return 100; } }

and now you get the correct result
work starting 1 work starting 0 work starting 2 work starting 3 work starting 4 work starting 5 work starting 6 work starting 7 work starting 8 work starting 9 Completed work 1 Completed work 2 Completed work 3 Completed work 0 Completed work 5 Completed work 4 Completed work 6 Completed work 7 Completed work 8 Completed work 9 Next work...

Comments

Popular posts from this blog

Why there is semicolon at the start of a JavaScript function?

Very often while reviewing the code for my team, I will come across a semicolon at the start of JavaScript function as show below ; (function () { 'use strict'; ...and I often wondered what purpose it served. Guess what. It is an insurance to make sure your script works fine when all other scripts are merged together;  The leading ; in front of immediately-invoked function expressions (iffe) is there to prevent errors when appending the file during concatenation to a file containing an expression not properly terminated with a ;. So there you go. Now you know what that little semicolon is doing there in your code.

What is Event Sourcing?

If you scratching your head and wondering what is Event Sourcing, this is a very good article to start. https://www.erikheemskerk.nl/event-sourcing-awesome-powerful-different/ I couldn't have explained it any better.