我认为我在线程方面有很好的处理能力,直到我遇到一个我希望将更新基准测试到不同网格的场景。
我创建了一个带有网格控件的窗口,将ObservableCollection
绑定到它并用5000行的某些复杂数据类型(不包含锁)填充它。
然后我使用
创建了一个任务 Task.Factory.StartNew()
这经历了一个非常紧凑的循环(1000万次迭代),更新了我ObservableCollection
中随机项的随机属性,当然每个属性都会引发INotifyPropertyChanged
事件。
现在由于更新都发生在后台线程上,我预计UI会更新,尽管很难跟上这个后台线程在紧密循环中旋转。
相反,UI冻结了几秒钟(但没有变成空白或产生常见的旋转光标),然后在后台线程完成后返回。
我的理解是,后台线程会在生成大量INPC时对核心造成沉重负担,每个INPC都会通过WPF运行时自动编组到UI线程。
现在UI线程什么也没做,所以我希望它能消耗所有这些INPC并更新网格,但事实并非如此;没有发生过一次更新。但是,当我使用Timer(而不是紧密循环)执行此操作时,它可以正常工作。
有人请告诉我UI线程在做什么吗?提前谢谢!
答案 0 :(得分:1)
如果您使用大量这样的已发送更新阻塞消息泵,则其他消息将无法获得处理的机会,从而导致“冻结”消息。你观察到的效果。
这里可以帮助的一件事是在UI控件上使用Data Virtualization,以便只实际绑定可见行并监听INPC更新。默认情况下会为DataGrid启用此功能,但如果您使用更自定义的方法来显示数据,则可能会出现问题。
尽管如此,这对于目前可见的项目的频繁修改不会有帮助,因为真正快速的火灾更新仍然会阻塞调度员。如果您有这样的用例,您可能希望稍微隔离您的视图模型对象,并且有办法批量处理'你的更新。一种方法是在进行一系列更新时有一种抑制通知的方法,然后在每个实例上调用RaisePropertyChanged(null)
(或INPC助手基类上的任何等效方法)来更新对该实例的所有绑定。
另一种机制是在一些其他层(视图模型实例所代表的任何模型对象)中进行数据更新,然后以明确定义的间隔将这些属性复制到视图模型类。为了快速更新背景数据,我经常使用轮询循环而不是触发事件,这只是因为事件的发生频率高于UI所关心的事件,并且它会减慢后台处理以不断发送所有这些不必要的通知。