4

I am tearing my hair off because of the following issue. I have this bit of code that loops through a list of objects and creates a processing task for each of them.

        IList<TileInfo> tiles = tileSource.Schema.GetTilesInView(extent, level);
        List<Task> renderingTasks = new List<Task>();
        foreach (TileInfo info in tiles) {
            renderingTasks.Add(Task.Factory.StartNew(new Action(delegate {
                Console.WriteLine(Task.CurrentId +"Info object"+ info.GetHashCode());
                             }
                         })));
        }

This code prints:

1Info object36963566
2Info object36963566
3Info object36963566
4Info object36963566
5Info object36963566
6Info object36963566
7Info object36963566
8Info object36963566
9Info object36963566
10Info object36963566
11Info object36963566
12Info object36963566
...

As you can see, the problem is that the tasks seem to all have a reference to one object!

Why are the tasks all using only one object from the list?

Thanks for you help

GETah
  • 20,922
  • 7
  • 61
  • 103

2 Answers2

5

I'll try and add some more detail in a moment, but it's about closing over the variable. Change you code to:

Essentially what you need to do, is create a new variable inside the loop, equal to the single declaration outside the inner workings of the loop.

IList<TileInfo> tiles = tileSource.Schema.GetTilesInView(extent, level);
List<Task> renderingTasks = new List<Task>();

foreach (TileInfo info in tiles) 
{
   TileInfo closingInfo = info;
   renderingTasks.Add(Task.Factory.StartNew(new Action(delegate {
                      Console.WriteLine(Task.CurrentId +"Info object"+ closingInfo.GetHashCode()); }})));
}

To Read more:

The foreach identifier and closures

http://blogs.msdn.com/b/ericlippert/archive/2009/11/12/closing-over-the-loop-variable-considered-harmful.aspx

Community
  • 1
  • 1
Ian
  • 33,605
  • 26
  • 118
  • 198
4

object36963566 must be last instance in your tiles list

This is because how the foreach works in current implementation of .Net. Although it will be fixed in future.

You need to read Closing over the loop variable considered harmful for understanding how foreach works in this case.

Task.CurrentId +"Info object"+ info.GetHashCode()

info in above code is referring to an item in the list tiles. The delegate you are creating will not use the item info was referring to when it(delegate) was created. Rather it will use the current value of info(value that info was pointing to when the method/delegate will actually run) which is obviously pointing to last item of the list. That's why you are getting this behaviour

Haris Hasan
  • 29,856
  • 10
  • 92
  • 122