System.Threading.Tasks in for循环问题

时间:2012-01-23 13:43:35

标签: c# multithreading task

由于以下问题,我正在撕掉我的头发。我有一些代码循环遍历对象列表并为每个对象创建一个处理任务。

        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());
                             }
                         })));
        }

此代码打印:

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

正如您所看到的,问题是任务似乎都引用了一个对象!

为什么这些任务都只使用列表中的一个对象?

谢谢你的帮助

2 个答案:

答案 0 :(得分:5)

我会尝试在一瞬间添加更多细节,但这是关闭变量。将代码更改为:

基本上你需要做的是在循环中创建一个新变量,等于循环内部工作之外的单个声明。

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()); }})));
}

阅读更多内容:

The foreach identifier and closures

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

答案 1 :(得分:4)

object36963566必须是您的图块列表中的最后一个实例

这是因为foreach在.Net的当前实现中的工作原理。虽然将来会修复。

您需要阅读Closing over the loop variable considered harmful以了解foreach在这种情况下的工作原理。

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

上面代码中的

info指的是列表tiles中的项目。您创建的delegate将不会使用info所指的项目(委托)创建时间。相反,它将使用info的当前值(信息在方法/委托实际运行时指向的值),这显然指向列表的最后一项。这就是你得到这种行为的原因