严格的MVVM和Task.ConfigureAwait(false)

本文关键字:ConfigureAwait false Task MVVM | 更新日期: 2023-09-27 18:25:45

我正在重写MVVM框架的某些部分,以利用异步/等待功能,请考虑以下VM代码:

private async Task LoadDossier(int ID)
    {
        //VM INotifyProperty
        Dossiers = new ObservableCollection<Dossier>(await BubManager.GetAllBubDossiersForEmployeeByDossierIdAsync(ID).ConfigureAwait(false));
        //VM INotifyProperty
        SelectedDossier = Dossiers.First(x => x.Id == ID);
        //VM INotifyProperty
        DossierEmployer = await EmployerManager.GetEmployerByIdAsync(SelectedDossier.EmployerId).ConfigureAwait(false);
        //VM INotifyProperty
        DossierEmployee = await EmployeeManager.GetEmployeeByIdAsync(SelectedDossier.EmployeeId).ConfigureAwait(false);
        //All VM INotifyProperties
        if (SelectedDossier != null && DossierEmployer != null)
        {
            RszNr = DossierEmployer.RszNr;
            FundsNr = DossierEmployer.FundsNr;
            RrNr = DossierEmployee.RrNr;
        }
        RefreshGlobalCommanding();
    }

由于我使用的是严格的MVVM,这里没有一个属性需要UI线程,因此我在任何地方都使用ConfigureAwait(false)。我知道在添加/删除ObservableCollection时这是不可能的,但这里的情况并非如此。

  • 这个代码是一个好的做法吗
  • 如果是,那么为什么不能将其作为默认行为呢?不断地键入ConfigureAwait(false)对可读性IMO没有帮助

编辑

经过与斯蒂芬的讨论,我得出的结论是,这基本上是一样的。

  • 如果您使用ConfigureAwait(false),那么您告诉WPF回调不必在UI线程上。设置INotifyProperty时,WPF会确保UI线程收到通知
  • 如果不这样做,WPF会确保回调在UI线程上,并且根本没有问题

这两种情况都使WPF在某个时刻进行UI线程编组。然而,我比较了两者的性能,发现有显著的差异。我执行了1000次循环,其中一个对象绑定到一个视图,然后再次设置为null,以下是以毫秒为单位的结果:

一个等待呼叫,任务延迟10ms

  • 带有ConfigureAwait(false)的15623ms
  • 51700ms无

三个等待呼叫,任务延迟10ms

  • 46917ms,带ConfigureAwait(false)
  • 82647ms无

这个基准测试远非完美,但将属性编组回UI线程似乎比强制等待调用后的所有内容在同一线程上运行成本更低。然而,这需要更多的测试来为所有场景给出明确的答案。

我把这个放在这里只是为了证明两者之间存在差异。我的建议是在这种情况下不要使用ConfigureAwait,因为它在更改代码时会增加异常的可能性,可读性较差,同事可能不理解这一点,并通过使用它获得虚假的安全感。

严格的MVVM和Task.ConfigureAwait(false)

这个代码是一个好的做法吗?

就我个人而言,我选择将所有数据绑定属性视为具有UI亲和性。

一个原因是不同的MVVM框架在这方面具有不同的能力;的确,WPF会为您处理这个问题(对于简单的属性),但其他人不会。

如果是,那么为什么不能将其作为默认行为呢?

async在CTP中时,关于await的这种默认行为有很多讨论。各有利弊。