严格的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,因为它在更改代码时会增加异常的可能性,可读性较差,同事可能不理解这一点,并通过使用它获得虚假的安全感。
这个代码是一个好的做法吗?
就我个人而言,我选择将所有数据绑定属性视为具有UI亲和性。
一个原因是不同的MVVM框架在这方面具有不同的能力;的确,WPF会为您处理这个问题(对于简单的属性),但其他人不会。
如果是,那么为什么不能将其作为默认行为呢?
当async
在CTP中时,关于await
的这种默认行为有很多讨论。各有利弊。