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

时间:2016-03-14 12:18:42

标签: c# .net asynchronous async-await .net-4.6

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

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

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

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

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

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

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关键字的方法)?为什么不在不Task PutTaskDelay的情况下返回整个async?我认为我应该尽可能直接返回Task,并使用async / await仅在应用层中获得最终结果。我对吗?如果没有,我在这里展示的两种方法有什么区别?

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

2 个答案:

答案 0 :(得分:8)

  

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

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

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

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

返回TaskTask<T>的方法表示异步操作。当您在图书馆时,建议您始终使用.ConfigureAwait(false),原因详见here。另外,我总是将人们指向this detailed article这个主题。

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

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

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

同样,此版本也会执行相同操作... ,但不需要async/await个关键字。它们不需要的原因是该方法本身不需要使用SignResponse,因此它可以简单地返回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;
}

答案 1 :(得分:-2)

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

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