Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

System.Threading.Tasks in a for loop issue

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

like image 845
GETah Avatar asked Jan 18 '23 17:01

GETah


1 Answers

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

like image 196
Ian Avatar answered Feb 06 '23 14:02

Ian