Dispatcher.beginInvoke只执行最后一次循环?

时间:2011-01-19 14:43:06

标签: c# threadpool dispatcher

我在Silverlight代码隐藏中有一段代码看起来像这样:

foreach (MapLocation loc in e.Result)
        {

            testDict[loc.ElemId] = loc.ToString();

            this.Dispatcher.BeginInvoke(delegate()
            {
                Image icon = new Image();
                icon.SetValue(Image.SourceProperty, nurseIconSource);
                Canvas.SetLeft(icon, (double)loc.X * MAP_SCALE);
                Canvas.SetTop(icon, MAP_HEIGHT - (double)loc.Y * MAP_SCALE);
                icons[loc.ElemId] = icon;
                MainCanvas.Children.Add(icon);
            });
        }
    }

此循环在与UI线程分开的线程上运行25次。执行该方法后,testDict对象以所有25个条目结束,而图标字典仅存储第25个(最后一个)项目的条目。

这是我第一次使用Dispatcher。它不是故意被称为快速射击吗?我能想到的是,第一次调用委托是在最后一次循环之后,因此loc对象始终是同一个项目。这准确吗?

2 个答案:

答案 0 :(得分:5)

这是旧的“不捕获循环变量”问题。这吸引了很多人。

基本上,当委托执行时总是使用{em>当前值loc ...如果你已经在委托执行之前完成了循环,那就意味着loc的最后一个值基本上会多次显示。

解决方案是获取循环变量的副本:

foreach (MapLocation loc in e.Result)
{
    MapLocation copy = loc;
    testDict[loc.ElemId] = loc.ToString();

    this.Dispatcher.BeginInvoke(delegate()
    {
        Image icon = new Image();
        icon.SetValue(Image.SourceProperty, nurseIconSource);
        Canvas.SetLeft(icon, (double)copy.X * MAP_SCALE);
        Canvas.SetTop(icon, MAP_HEIGHT - (double)copy.Y * MAP_SCALE);
        icons[copy.ElemId] = icon;
        MainCanvas.Children.Add(icon);
    });
}

请注意在匿名方法中使用“copy”而不是“loc”。

有关详细信息,请阅读Eric Lippert关于“关闭环路变量被视为有害”的博客文章 - part one; part two

答案 1 :(得分:1)

您正在关闭迭代变量。解决方案是将loc分配给循环内的临时变量。

    foreach (MapLocation location in e.Result)
    {
        //assign temp variable here.
        MapLocation loc = location;
        //...
    }