15

I was experimenting with tasks. Why does this output 10 and not each value of the loop?

public static void StartTasks()
{
    Task[] tasks = new Task[10];
    for (int i = 0; i < 10; i++)
        tasks[i] = new Task(() => Console.WriteLine(i));

    foreach (Task task in tasks)
    {
        task.Start();                       
    }       
}
Marty
  • 169
  • 1
  • 1
  • 4

2 Answers2

21

C# lambdas capture a reference to the variable, not the value of the variable.

If you want to capture the value, you need to make a copy of it first inside the loop which causes the capture to get the reference to the locally scoped unchanging variable.

public static void StartTasks()
{
    Task[] tasks = new Task[10];
    for (int i = 0; i < 10; i++) {
        int j = i;
        tasks[i] = new Task(() => Console.WriteLine(j));
    }

    foreach (Task task in tasks)
    {
        task.Start();                       
    }       
}
Donnie
  • 45,732
  • 10
  • 64
  • 86
6

In addition to the accepted answer, you can also pass a parameter to the task. For example,

    using System;
    using System.Threading.Tasks;

    static void StartTasks(int instances)
    {
        var tasks = new Task[instances];
        for (int i = 0; i < instances; i++)
        {
            tasks[i] = new Task((object param) =>
            {
                var t = (int)param;
                Console.Write("({0})", t);
            }, i);
        }

        Parallel.ForEach<Task>(tasks, (t) => { t.Start(); }); 
        Task.WaitAll(tasks);
    }
Alex Nolasco
  • 18,750
  • 9
  • 86
  • 81