我开始在我们的 .Net WPF 应用程序中使用 await/async 机制。
在我的 ViewModel 中,我在服务上调用异步方法。
我的问题是: 是不是比较好
- 直接在这个服务里面做一个大的
return await Task.Run(()=>{...});
- 此服务上的所有子方法是否也是异步的,然后在其中有
Task.Run
?
举例:
1)
public class Service:IService{
public async Task<SomeResult>(SomeParameter parameter){
return await Task.Run(()=>{
CopyStuff(parameter.A);
UpgradeStuff(parameter.B);
return ReloadStuff(parameter.C)
});
}
private void CopyStuff(ParamA parameter){
...//Some long operation that will mainly wait on the disk
}
private void UpgradeStuff(ParamB parameter){
...//Some long operation that should not block the GUI thread
}
public SomeResult ReloadStuff(ParamC parameter){
return ...;//Some long operation that relaunch some services and return their successs
}
}
2)
public class Service:IService{
public async Task<SomeResult>(SomeParameter parameter){
await CopyStuff(parameter.A);
await UpgradeStuff(parameter.B);
return await ReloadStuff(parameter.C)
}
private async Task CopyStuff(ParamA parameter){
return await Task.Run(()=>{...});//Some long operation that will mainly wait on the disk
}
private async Task UpgradeStuff(ParamB parameter){
return await Task.Run(()=>{...});//Some long operation that should not block the GUI thread
}
public async Task<SomeResult> ReloadStuff(ParamC parameter){
return await Task.Run(()=>{return ...});//Some long operation that relaunch some services and return their successs
}
}
我可以看到两种方法的优点:
- 在 1) 中我们将使用较少的任务,这可能是最有效的(???)
- 在 2) 中,这感觉更“符合”异步等待方法,这将允许更改某些方法的可见性并仍然是异步的,如果有一天需要,这将允许这些方法并行运行。<
最佳答案
选择哪个选项?
我不会使用你的任何一个选项,它们都会创建一个误导性的 API,每个将使用你的服务的人都会认为他使用了异步方法,但事实是在虚假签名的背后方法实际上根本不是异步的。
您的服务只是将工作推送到另一个 ThreadPool
线程,该线程将在方法执行期间被阻塞。
虽然在客户端听起来并没有那么糟糕,但在服务器端使用此原则确实会损害您的可扩展性。
根据斯蒂芬·克莱里的说法:
do not use Task.Run in the implementation of the method; instead, use Task.Run to call the method.
如果方法真的是同步的,你不应该用伪造的异步签名包装你的服务方法,如果你不想在执行重方法时阻塞 UI 线程,你应该在调用 View 模型中的服务方法。
我建议你在他的博客中阅读 Stephen Cleary 系列的 Task.Run Etiquette articles。
考虑对 I/O 操作使用异步方法
此外,我可以看到您的服务所做的工作不仅是 CPU 绑定(bind)工作,如果是这样的话,如果您现在使用的同步方法有任何可用方法(例如 Asynchronous File I/O),您应该考虑使用内置的 I/O 异步 API 方法),在这种情况下,您的方法将是真正的异步方法,而不是像现在这样伪造的异步包装器。
如果您这样做,您的 UI 线程将不会在执行 I/O 异步操作时阻塞,但如果仍然涉及大量 CPU 绑定(bind)工作并且您不想在 CPU 绑定(bind)工作执行期间阻塞 UI,您可以从 View 模型调用服务方法时仍然使用 Task.Run(即使方法签名已经是异步方法签名)。
在上面的系列文章中详细介绍了同步和异步方法的混合。
在异步 API 方法中使用 bult 的另一个巨大优势是,如果该方法是真正异步的,您可以在执行异步 I/O 操作时使用 do not block 任何 ThreadPool
线程以及更多 ThreadPool
线程可以自由地做任何其他工作。
这在服务器端使用异步编程时尤其(但不仅)重要,它可以真正提高您的可扩展性。
异步和 MVVM
最后一件事,如果您遵循 MVVM 模式,MSDN "async MVVM" articles 是您可以使用的很好的阅读 Material 。
关于c# - 使用 await-async 的最佳实践,从哪里开始任务?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40104279/