假设ChartViewModels
是ObservableCollection<T>
,以下代码将按预期工作:
await Dispatcher.InvokeAsync(() =>
{
ChartViewModels.Clear();
ChartViewModels.AddRange(initializedCharts);
}, DispatcherPriority.DataBind, mCancellationToken.Token);
await UpdateChartsWithValuesAsync(chartsToInitialize, ChartViewModels).ConfigureAwait(false);
相反,如果我将UpdateChartsWithValuesAsync
方法调用包装在ContinueWith
委托中,则不再等待该方法。我已经尝试将ConfigureAwait(false)
更改为true
,但似乎没有任何变化。在修改后的代码下方:
await Dispatcher.InvokeAsync(() =>
{
ChartViewModels.Clear();
ChartViewModels.AddRange(initializedCharts);
}, DispatcherPriority.DataBind, mCancellationToken.Token).Task
.ContinueWith(async t => await UpdateChartsWithValuesAsync(chartsToInitialize, ChartViewModels).ConfigureAwait(false), mCancellationToken.Token, TaskContinuationOptions.OnlyOnRanToCompletion, TaskScheduler.Current).ConfigureAwait(false);
Dispatcher
中的代码总是在ContinueWith
委托之前执行,但不再等待UpdateChartsWithValuesAsync
完成,这会引起讨厌的错误。
任何人都可以解释这种行为吗?谢谢
WPF,.NET Framework 4.7项目
简单地说,.ContinueWith()
在其实现中不执行await
,而是按原样运行传入的委托并返回任务任务(Task<Task>>
)。由于不等待传入的委托,所以此外部任务立即完成。
我的建议,在这种情况下,请勿使用.ContinueWith()
,只需坚持等待即可。如果您真的想保留当前代码,则可以执行.ContinueWith().Unwrap()
,这将解决问题。
这也是该主题中的另一个相关问题:Use an async callback with Task.ContinueWith
如果您想进一步了解,请参阅ContinueWith
的源代码:https://referencesource.microsoft.com/#mscorlib/system/threading/tasks/Task.cs,4532
Dispatcher.InvokeAsync
返回DispatcherOperation
。这是一个等待的类型,因为它实现了一个称为GetAwaiter()
的方法,该方法返回实现了INotifyCompletion
的类型。
当直接在await
的结果上使用Dispatcher.InvokeAsync
时,在调用DispatcherOperation
之前要等待UpdateChartsWithValuesAsync
的完成。
在第二个示例中,您不直接等待;您正在等待链接表达式的结果:
Dispatcher
.InvokeAsync() // returns DispatcherOperation
.Task // returns Task
.ContinueWith(); // returns another Task
因此仅等待最终对象(Task
),这意味着可以在ContinueWith
完成之前执行传递给Dispatcher.InvokeAsync
的函数。
如果使用的是异步/等待,则仅在异步方法中使用关键字是谨慎的做法,因为与基于回调的操作混合会导致诸如此类的代码混乱。
正如提到的其他答案,您不应该将async t => await UpdateChartsWithValuesAsync
放在您的ContinueWith
回调中,因为它会导致等待Task<Task> ContinueWith(...)
方法,该方法最终会立即完成。
如果您真的想在大多数外部await
中等待ContinueWith完成,则应该Unwrap()
您的Task<Task>
并等待,或者在内部使用同步API,但请记住有关同步上下文的正确管理。
await Dispatcher.InvokeAsync(() =>
{
ChartViewModels.Clear();
ChartViewModels.AddRange(initializedCharts);
}, DispatcherPriority.DataBind, mCancellationToken.Token).Task
.ContinueWith(t => UpdateChartsWithValuesAsync(), mCancellationToken.Token, TaskContinuationOptions.OnlyOnRanToCompletion, TaskScheduler.Current)
.Unwrap()
.ConfigureAwait(false);
请注意,await
被翻译成包含ContinueWith
的代码块,该代码块具有ConfigureAwait
选项和更多功能,因此,最好像在第一个示例中那样使用async await
构造。