使用await-async的最佳做法,在哪里开始任务?

时间:2016-10-18 09:19:30

标签: c# .net wpf asynchronous

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

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

我的问题是:

更好吗?
  1. 直接在此服务中,制作一个大return await Task.Run(()=>{...});
  2. 让此服务上的所有子方法也是异步的,然后在其中包含Task.Run
  3. 以示例:

    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方法更加“兼容”,这将允许更改某些方法的可见性并仍然是异步的,这将允许方法在需要的一天内并行运行。

1 个答案:

答案 0 :(得分:9)

选择哪个选项?

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

虽然在客户端使用此原则在服务器端听起来不那么糟糕,但这确实会损害您的可扩展性。

Stephen Cleary说:

  

在方法的实现中不要使用Task.Run;相反,使用   Task.Run调用方法。

如果方法实际上是同步的,你不应该使用假异步签名包装你的服务方法,如果你不想在重方法执行时阻止UI线程,你应该使用Task.Run您正在从视图模型中调用服务方法。

我建议你在博客中阅读Stephen Cleary系列Task.Run Etiquette articles

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

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

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

更多关于上述系列文章中同步和异步方法的混合。

在异步API方法中使用bult的另一个巨大优势是,如果该方法在执行异步I / O操作时Asynchronous File I/O任何ThreadPool线程真正异步,并且{{1} {{1}线程可以自由地做任何其他工作 在服务器端使用异步编程时,这尤其(但不仅仅)很重要,它可以真正提高您的可扩展性。

异步和MVVM

最后一件事,如果您关注MVVM模式do not block,那么您可以使用它。