始终在库中的异步方法中使用“async”和“await”关键字?

简介 :在库方法中,何时应该使用asyncawait关键字而不是直接返回Task

我相信我的问题与此问题有关。 但是,这个问题是关于.NET 4.0和TPL,而我正在使用带有asyncawait关键字的.NET 4.6。 所以,我认为我的问题可能得到不同的答案,因为在回答链接问题时这些关键字不存在。

说明:我正在为外部WCF服务编写一个简单的包装器,并且包装器会进行多个SendAsync调用。 现在我认为每个包装器方法应该直接返回一个Task而不需要等待。 我的理解是应该在应用程序层上使用async / await ,而不是在库中。

因此,例如,我认为我应该为每个包装器方法采用的方法:

 private Task GetSignDataAsync(SigningRequestType request) { return _service.SendAsync(request); } 

但是在互联网上,我找到了几个使用这种方法的post

 private async Task GetSignDataAsync(SigningRequestType request) { return await _service.SendAsync(request).ConfigureAwait(false); } 

这是我在technet上找到的另一个例子:

 async Task PutTaskDelay() { await Task.Delay(5000); } private async void btnTaskDelay_Click(object sender, EventArgs e) { await PutTaskDelay(); MessageBox.Show("I am back"); } 

那么,我何时应该使用第二种方法(包括asyncawait关键字的方法)? 为什么不在不使PutTaskDelay async情况下返回整个Task ? 我认为我应该在可能的情况下直接返回Task ,并使用async / await仅在应用层中获得最终结果。 我对吗? 如果没有,我在这里展示的两种方法有什么区别?

我担心 :当使用asyncawait关键字时,它似乎只是为编译器提供了额外的工作而没有任何好处。

我应该在库中使用async await吗?

这完全取决于。 如果您要利用异步编程范例,那么答案是“是”, 大多数时候都需要asyncawait关键字。 更有可能的是,您会发现自己需要使用async/await 。 这是因为在大多数情况下,仅使用TaskTask是很困难的,因为您很可能需要推断您调用的异步操作的结果。

此外,根据您的问题,似乎您可能对关键字本身以及它们与TaskTask类型的关系有些混淆。 请允许我为您澄清一下。

async关键字允许方法使用await关键字。 最佳做法是让所有异步方法返回TaskTask 除非您无法(例如,如上所示的按钮单击事件处理程序)。

返回TaskTask表示异步操作。 当您在库中时,建议始终使用.ConfigureAwait(false) ,原因如下所述。 另外,我总是向人们指出关于这个主题的详细文章 。

要区分问题中的两种方法:

下面的方法返回一个Task 。 这是一个异步操作,表示要登录的工作。调用者可以等待该方法获取SignResponse

 private Task GetSignDataAsync(SigningRequestType request) { return _service.SignAsync(request); } 

同样,这个版本做同样的事情…… 除了不需要async/await关键字 。 它们不需要的原因是该方法本身不需要使用SignResponse ,因此它可以简单地返回Task如上所示。 正如您在问题中指出的那样,在不需要时使用async/await关键字确实会受到惩罚。 这样做会增加一个额外的状态机步骤,因为它等待着结果。

 private async Task GetSignDataAsync(SigningRequestType request) { return await _service.SignAsync(request).ConfigureAwait(false); } 

最后,如果您需要推理响应,可以使用上述关键字来执行此操作:

 private async Task GetSignDataAsync(SigningRequestType request) { var result = await _service.SignAsync(request).ConfigureAwait(false); if (result.SomeProperty == SomethingWeCareToCheck) { _log.Log("Wow, this was un-expected..."); } return result; } 

不要接受我的话,因为我从来没有理解async /等待那么好,但最让我困扰的是,所有使用async的方法都必须被标记为async,这让我感到厌烦。

我想在一个库中,人们可以选择如何使用这些方法是一件好事,所以你应该使用async,但我总是发现直接明确地使用Tasks更清楚。