等待 Task.CompletedTask 是为了啥?

Posted

技术标签:

【中文标题】等待 Task.CompletedTask 是为了啥?【英文标题】:await Task.CompletedTask for what?等待 Task.CompletedTask 是为了什么? 【发布时间】:2017-10-21 03:08:18 【问题描述】:

我使用在 Build2017 中介绍的 Windows Template Studio 创建了 UWP 应用。

下面的类是它生成的代码的一部分。

public class SampleModelService

    public async Task<IEnumerable<SampleModel>> GetDataAsync()
    
        await Task.CompletedTask; // <-- what is this for?
        var data = new List<SampleModel>();

        data.Add(new SampleModel
        
            Title = "Lorem ipsum dolor sit 1",
            Description = "Lorem ipsum dolor sit amet",
            Symbol = Symbol.Globe
        );

        data.Add(new SampleModel
        
            Title = "Lorem ipsum dolor sit 2",
            Description = "Lorem ipsum dolor sit amet",
            Symbol = Symbol.MusicInfo
        );
        return data;
    

我的问题是,这里的await Task.CompletedTask; 代码的目的和原因是什么?它实际上没有来自它的Task 结果接收器。

【问题讨论】:

【参考方案1】:

它可以让后期更容易实现异步代码调用,而无需更改签名,从而避免重构调用代码。

虽然脚手架示例代码是同步的,但 Template Studio 是专门围绕 async 数据访问层设计的,您应该通过修改生成的方法的主体来实现自己的数据访问。

如果 async 实现实现,整个模板化应用程序中的代码都会发生重大变化,对于新开发人员来说,这将是一个非常陡峭的学习曲线,重点是模板的核心是用最少的努力甚至经验来启动和运行!

另一种选择是从方法签名和该行中删除 async 关键字并执行

return Task.FromResult<IEnumerable<SampleModel>>(data); 

当你需要返回一个等待的任务时,你会看到这个构造,例如,当实现没有异步工作要做时。

然而,在这种情况下,由于它是一个模板,他们希望人们将await Task.Completed 替换为await FetchDataFromDatabaseAsync(); 之类的东西。由于async 关键字已经存在,它最大限度地减少了实现您自己的异步调用所需的更改。

无论如何,如果没有这个 await 构造,你可以这样做:

public class SampleModelService

    public Task<IEnumerable<SampleModel>> GetDataAsync()
    
        var data = new List<SampleModel>();

        data.Add(new SampleModel
        
            Title = "Lorem ipsum dolor sit 1",
            Description = "Lorem ipsum dolor sit amet",
            Symbol = Symbol.Globe
        );

        data.Add(new SampleModel
        
            Title = "Lorem ipsum dolor sit 2",
            Description = "Lorem ipsum dolor sit amet",
            Symbol = Symbol.MusicInfo
        );

        return Task.FromResult<IEnumerable<SampleModel>>(data); 
     

如果根本不需要返回任务(您没有任何异步代码),那么只需将其完全删除。 (但是你必须重构调用这个方法的代码)

查看此代码,我怀疑有人会在开发过程的后期调用异步方法,并且已经预料到通过指定此方法返回 Task

【讨论】:

所以我们可以说,它是async签名的一种占位符。对吗? 好吧,每当您await 某些东西时,您都需要将async 添加到方法签名中。有关为什么需要 async 关键字的更深入解释,请阅读这篇文章:blogs.msdn.microsoft.com/ericlippert/2010/11/11/…。在这种特殊情况下,添加 async 关键字是合理的。由于这是一个模板,他们希望人们将 await Task.Completed 替换为 await FetchDataFromDatabaseAsync(); return Task.Run(()=>xx )? 消费者不会对异常处理语义有期望吗?如果代码抛出异常会发生什么?通常它应该返回一个错误的任务,但在这种情况下它不会;异常会冒泡。 @rory.ap 正确,以减轻您可以捕获异常并返回 Task.FromException(exception);【参考方案2】:

await Task.CompletedTask 如第一个答案所说,使实现此方法更容易,因为此实现不耗时,因此“await Task.CompletedTask;”或“返回 Task.FromResult(data);”两者都将同步运行。您可以使用上面提到的这两种模式,但是这种方法被设计为异步方法,因此当您的实现耗时但没有任何异步代码时,请使用 Task.Run 来创建一个任务。

【讨论】:

以上是关于等待 Task.CompletedTask 是为了啥?的主要内容,如果未能解决你的问题,请参考以下文章

30.超时设置

web自动化的三大等待

线程系列三线程的等待与唤醒机制

MySQL锁等待与死锁问题分析

6.测试脚本中的等待方法

AsyncTask - 等待其他 Asynctask 的执行