在后台更新列表时,为什么UI线程会挂起?

时间:2014-12-09 22:14:15

标签: c# wpf multithreading

我认为我在线程方面有很好的处理能力,直到我遇到一个我希望将更新基准测试到不同网格的场景。

我创建了一个带有网格控件的窗口,将ObservableCollection绑定到它并用5000行的某些复杂数据类型(不包含锁)填充它。

然后我使用

创建了一个任务
 Task.Factory.StartNew()

这经历了一个非常紧凑的循环(1000万次迭代),更新了我ObservableCollection中随机项的随机属性,当然每个属性都会引发INotifyPropertyChanged事件。

现在由于更新都发生在后台线程上,我预计UI会更新,尽管很难跟上这个后台线程在紧密循环中旋转。

相反,UI冻结了几秒钟(但没有变成空白或产生常见的旋转光标),然后在后台线程完成后返回。

我的理解是,后台线程会在生成大量INPC时对核心造成沉重负担,每个INPC都会通过WPF运行时自动编组到UI线程。

现在UI线程什么也没做,所以我希望它能消耗所有这些INPC并更​​新网格,但事实并非如此;没有发生过一次更新。但是,当我使用Timer(而不是紧密循环)执行此操作时,它可以正常工作。

有人请告诉我UI线程在做什么吗?提前谢谢!

1 个答案:

答案 0 :(得分:1)

如果您使用大量这样的已发送更新阻塞消息泵,则其他消息将无法获得处理的机会,从而导致“冻结”消息。你观察到的效果。

这里可以帮助的一件事是在UI控件上使用Data Virtualization,以便只实际绑定可见行并监听INPC更新。默认情况下会为DataGrid启用此功能,但如果您使用更自定义的方法来显示数据,则可能会出现问题。

尽管如此,这对于目前可见的项目的频繁修改不会有帮助,因为真正快速的火灾更新仍然会阻塞调度员。如果您有这样的用例,您可能希望稍微隔离您的视图模型对象,并且有办法批量处理'你的更新。一种方法是在进行一系列更新时有一种抑制通知的方法,然后在每个实例上调用RaisePropertyChanged(null)(或INPC助手基类上的任何等效方法)来更新对该实例的所有绑定。

另一种机制是在一些其他层(视图模型实例所代表的任何模型对象)中进行数据更新,然后以明确定义的间隔将这些属性复制到视图模型类。为了快速更新背景数据,我经常使用轮询循环而不是触发事件,这只是因为事件的发生频率高于UI所关心的事件,并且它会减慢后台处理以不断发送所有这些不必要的通知。