不在UI线程上调用OnPropertyChanged - 性能降级

时间:2018-04-23 09:15:02

标签: c# wpf multithreading

我已经通过缓慢的UI更新解决了一些问题,我希望听到解释为什么我的代码修改实际上提高了性能。

我开发了一个WPF应用程序,即MVVM架构。 我在视图模型中有一个属性:

public DateTime MyDate
    {
        get
        {
            return _myDate;
        }
        set
        {
            _myDate= value;
            OnPropertyChanged()
            OnPropertyChanged(() => MyDateFormatted);
        }
    }

和MyDateFormatted看起来如下:

public string MyDateFormatted
    {
        get { return _myDate.ToString("MMMM dd, yyyy"); }
    }

MyDate的setter不会发生在UI线程上。 MyDateFormatted的getter发生在UI Thread上,因为正如我所读到的,WPF会自动将属性更改封送到UI线程。

Making sure OnPropertyChanged() is called on UI thread in MVVM WPF app

事实上,用户界面已更新,但速度非常慢。 有一次,我手动连接了 OnPropertyChanged(()=> MyDateFormatted); 到UI Dispacher,通过调用它来包围它: Application.Current.Dispatcher, UI现在可以尽快更新,并且性能得到了显着提升。你能解释一下原因吗?谢谢!

1 个答案:

答案 0 :(得分:2)

一个可能的原因是Dispatcher.Invoke限制(减慢)更新MyDate的算法,并允许UI线程跟上这些更新。假设您有以下代码:

new Thread(() => {
    while (true) {
        this.MyDate = DateTime.UtcNow;
    }
})
{ 
   IsBackground = true 
}.Start();

这是一种极端情况,其中值不断更新。当调用OnPropertyChanged MyDate时,WPF将通过Dispatcher.BeginInvoke(注意Begin)在UI线程上对绑定更新进行排队。

在极端情况下,它会非常快地泛滥UI线程队列,并且UI线程将无法跟上要处理的待处理操作的数量。那是因为Dispatcher.BeginInvoke不会减慢while (true)线程的速度,因为它是异步的。

现在假设您将this.MyDate = DateTime.UtcNow包裹在Dispatcher.Invoke中。 Invoke是同步的,直到UI线程实际执行此操作才会返回。现在循环被限制并且运行得慢得多,因为更新UI现在是循环的一部分,并且UI动作队列不会无限制地增长。

在现实生活中,自己值得限制UI更新。因此,如果你有一个紧凑的循环 - 不要在每次迭代时更新UI绑定属性,而是在每次X(100,1000)迭代时更新,以免给UI线程带来太多负担(过快的更新无论如何都是无用的,因为人眼无论如何都无法抓住你的1ms更新)。如果您没有太多理由(执行UI更新)会降低算法速度,那么您的“修复”可能没问题。