c# task acting on later reference

for(int i = 0; i < list.Count(); i++)
{
  Task task = new Task(() => DoWork(list[i]));
  task.Start();
}

Can someone tell me how I can ensure that the DoWork function will act on the list at the position i?

I am having an issue where the task will start at some point after the next iteration of the for loop so I get duplicate threads essentially.

Jon Skeet
people
quotationmark

Yes, the variable is captured by the lambda expression, and you're then modifying it. There are a few simple options:

First, copying the variable to a variable which is instantiated within the loop:

for (int i = 0; i < list.Count; i++)
{
    int copy = i;
    Task task = new Task(() => DoWork(list[copy]));
    task.Start();
}

Now each iteration will capture a different copy variable, which isn't modified elsewhere.

Second, introducing a local variable but with list[i] instead:

for (int i = 0; i < list.Count; i++)
{
    var item = list[i];
    Task task = new Task(() => DoWork(item));
    task.Start();
}

Third, and only if you're using C# 5 or later, using foreach:

foreach (var item in list)
{
    Task task = new Task(() => DoWork(item));
    task.Start();
}

In earlier versions of C# that would have the same problem as your original code, as a single item variable would change in each iteration. From C# 5 onwards, each iteration of the foreach loop introduces a new iteration variable.

As a side note, from .NET 4.5 onwards it's typically simpler to use Task.Run instead of creating a new task and then starting it.

people

See more on this question at Stackoverflow