为了提高WrapPanel的性能,我在WP7应用程序中引入了线程。在一个列表中有一个长的List
的Item
-对象,一个接一个地添加到另一个"列表"中。我有以下两个列表:
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
的情况下完成。