使用wait-async的最佳实践,在哪里启动任务

本文关键字:在哪里 启动 任务 wait-async 最佳 使用 | 更新日期: 2023-09-27 18:13:25

我开始在。net WPF应用程序中使用await/async机制。

在我的ViewModel中,我调用了一个服务上的异步方法。

我的问题是:是否

  1. 直接在这个服务内部,创建一个大的return await Task.Run(()=>{...});
  2. 这个服务上的所有子方法也是异步的,然后里面有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)这感觉更符合async-await方法,这将允许改变一些方法的可见性,仍然是异步的,这将允许方法并行运行,如果需要的一天。

使用wait-async的最佳实践,在哪里启动任务

选择哪个选项?

我不会使用你的两个选项,它们都会创建一个误导性的API,每个使用你的服务的人都会认为他使用异步方法,但事实是在错误签名的背后,这些方法实际上根本不是异步的。
你的服务只是将工作推送到另一个ThreadPool线程,该线程将在方法执行期间被阻塞。

虽然在客户端听起来并没有那么糟糕,但在服务器端使用这个原则确实会损害您的可伸缩性。

根据Stephen Cleary:

不使用任务。运行中实现的方法;相反,使用的任务。

如果你的服务方法真的是同步的,你不应该用假的异步签名来包装你的服务方法,如果你不想在heavy方法执行的时候阻塞UI线程,你应该使用Task。当您从视图模型调用服务方法时运行。

我建议你读读Stephen Cleary的《任务》系列。在他的博客上运行礼仪文章

考虑使用异步方法进行I/O操作

此外,我可以看到你的服务所做的工作不仅仅是CPU绑定的工作,如果是这样,你应该考虑使用内置的I/O异步API方法,如果有任何可用的同步你现在使用(例如异步文件I/O),在这种情况下,你的方法将是一个真正的异步方法,而不是一个假的异步包装,因为他们现在。

如果你这样做,你的UI线程将不会阻塞,而I/O异步操作将执行,但如果仍然有繁重的CPU绑定工作涉及,你不想在CPU绑定工作执行期间阻塞UI,你仍然可以使用Task。当你从视图模型调用服务方法时运行(即使方法签名已经是异步的)。

关于同步和异步方法混合的更多信息,请参阅上述系列文章。

使用内置异步API方法的另一个巨大优势是,如果该方法是真正异步的,则在执行异步I/O操作时不会阻塞任何ThreadPool线程,并且更多的ThreadPool线程可以自由地做任何其他工作。
在服务器端使用异步编程时,这一点尤其重要(但不仅如此),它可以真正提高您的可伸缩性。

Async and MVVM

最后一件事,如果你遵循MVVM模式MSDN"异步MVVM"的文章是一个很好的阅读材料,你可以使用