总是在库的异步方法中使用 'async' 和 'await' 关键字?

Always use the 'async' and 'await' keywords in asynchronous methods in a library?

总结:在库方法中,我什么时候应该使用 asyncawait 关键字而不是 returning a Task 直接?

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

说明: 我正在为外部 WCF 服务编写一个简单的包装器,该包装器进行多次 SendAsync 调用。现在我认为每个包装器方法应该直接 return 一个 Task<> 而不被等待。我的理解是async/await应该用在应用层,不是在库里

所以,例如,这是我认为我应该为每个包装器方法采用的方法:

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

但是在 Internet 上,我发现 几个帖子 改用了这种方法:

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

这是我发现的另一个例子 on 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 关键字的方法)?为什么不只 return 整个 Task 而不 PutTaskDelay async?我觉得应该尽可能直接returnTask,只在应用层使用async/await得到最终结果。我对吗?如果不是,我在这里展示的两种方法有什么区别?

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

不要相信我的话,因为我从来没有那么理解 async/await,但这个想法最让我困扰的是所有使用异步的方法也必须标记为异步,这让我恼火不已。

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

Should I use async await in library?

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

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

async keyword allows a method to use the await关键字。最佳做法是让所有异步方法 return TaskTask<T> 除非 你不能(例如,按钮单击事件处理程序正如你在上面所展示的那样)。

returnTaskTask<T>表示异步操作的方法。当您在图书馆时,建议始终使用 .ConfigureAwait(false),原因详述 here. Additionally, I always point people to this detailed article 关于该主题。

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

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

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

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

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

最后,如果您需要对响应进行推理,您可以使用上述关键字来这样做:

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