线程、列表和循环不是一个好主意



为了提高WrapPanel的性能,我在WP7应用程序中引入了线程。在一个列表中有一个长的ListItem-对象,一个接一个地添加到另一个"列表"中。我有以下两个列表:

public List<Item> OriginalItems;
public List<Item> CopyOfItems;

放在BackgroundWorker.DoWork事件处理程序中的逻辑如下:

workerThread.DoWork += new DoWorkEventHandler((object sender, DoWorkEventArgs e) =>
{
    foreach (var item in OriginalItems)
    {
        Deployment.Current.Dispatcher.BeginInvoke(() =>
        {
            CopyOfItems.Add(item);
        });
        // I feel sooo sleepy
    }
});

现在,当我用Thread.Sleep(150)替换我的注释时,这很好——但任何更少的内容(偶尔甚至用更大的值)都会使代码连续多次放在同一个元素中。

为什么会出现这种情况,如何修复?

这是C#中的一个已知褶皱,实际上是在C#5中修复的。当您从lambda表达式中的foreach循环捕获循环变量时,您捕获的是一个变量。该变量通过循环更改其值,因此,如果在"原始"迭代完成后执行从lambda表达式创建的委托,则会看到"当前"迭代中的值。

一个简单的解决方法是在循环中声明和初始化迭代变量的副本,并捕获它:

foreach (var item in OriginalItems)
{
    var copy = item;
    Deployment.Current.Dispatcher.BeginInvoke(() =>
    {
        CopyOfItems.Add(copy);
    });
    // I feel sooo sleepy
}

请参阅Eric Lippert的博客文章"关闭被认为有害的循环变量",了解更多关于这方面的详细信息。

顺便说一句,你的真实代码真的在循环中做任何工作吗?除了将UI线程工作拆分为几个块之外,还不清楚您是否真的在使用线程来做任何重要的事情——这可以在没有BackgroundWorker的情况下完成。

最新更新