在主线程上执行异步任务的最佳方式

4

我想知道在.NET应用程序中(确切地说,在ViewModel中),执行主线程任务的最佳异步方式是什么。Dispatcher.BeginInvoke现在是否仍然有效,或者有更好的方法来实现这一点?


我非常确定您不想在视图模型中引入多线程,可能是在控制器中使用 async ... await 构造。 - garryp
好的。在ViewModel中使用一些async/await有问题吗? - user4856537
1
在视图模型中使用线程(或者异步等待)没有任何问题。 - BradleyDotNET
我不同意。我无法想象一个视图模型执行的任务会证明引入多线程是合理的。如果它们正在执行这样的任务,那么很可能该任务放错了位置。 - garryp
1
@garryp 从模型中加载数据怎么样?如果我的模型是一个WCF代理,也许它有可等待的方法(实际上有这个)。即使你将await抽象成一个“Fire and forget”方法,VM仍然有效地引入了线程。我认为你对涉及的交互的看法有点过于简单化,但我可能很容易错 :). - BradleyDotNET
1
很难不使用非常具体的例子,但我认为模型不应向相对高层次的视图模型暴露这种信息。我猜你想到的是一个相当聪明的模型,而我更喜欢相当愚笨的模型,仅由服务和助手封装一些调用WCF服务的内容。 - garryp
2个回答

6

BeginInvoke是一种非常底层的方法,通常情况下,您可以使用async/await从UI线程启动异步操作,并自动返回到UI线程显示结果。默认情况下,UI上下文由await捕获并用于在等待的操作完成后恢复async方法。(我在我的博客中更详细地描述了这个过程on my blog)。

如果您需要从UI中显示进度报告,则最佳选项是IProgress<T>/Progress<T>类型,它们也会为您执行所有线程处理的工作。

最后,如果您确实需要使用不断更新的数据来更新UI,则最佳选项是使用响应式扩展并在捕获的SynchronizationContext上进行观察。

这些选项是从最常见到最不常见的。我估计90%的用例只需要async/await,99%的用例需要async/await + IProgress<T>,而100%的用例需要Rx。我从未遇到过需要使用BeginInvoke的情况,即使使用它也没有好处。


非常好的回答。谢谢! - user4856537

2
如果您需要将操作调度到UI线程上,是的,BeginInvoke仍然是正确的方法。
如果您已经在UI线程上,但不想让操作阻塞您,可以使用await/async。请注意,如果正在执行的工作实际上不是异步的,并且只是将其包装在Task.Run中,这仍然可能会阻塞UI线程。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接