且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

始终使用“异步”,并在库异步方法'等待'关键字?

更新时间:2022-10-30 20:03:06


  

我应该使用异步库等待?


块引用>

这一切都依赖。如果你打算采取异步编程范式的优势,那么答案是是的异步的await 需要使用关键字的的大部分时间的。更可能的,你会发现自己需要使用异步/的await 。这是因为在大多数情况下,这将是很难只用工作任务< T> ,你将超过可能需要推理,你调用异步操作的结果。

此外,根据您的问题,它好像你可能对关键字本身有些混乱,他们如何与工作任务&LT ; T> 类型。请允许我澄清这个给你。

异步 / A>关键字允许的方法来使用 的await 关键字。***的做法是让所有异步方法返回为任务任务< T> 除非您无法(例如,当你上面展示了一个按钮单击事件处理)。

返回方法工作任务< T> 重新present异步操作。当你在一个库,建议总是使用 .ConfigureAwait(假)的详细原因的此处。此外,我总是指向人关于这个问题这方面的详细文章

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

下面的方法返回一个任务< SignResponse> 。这是一个异步的操作,重新presents工作签到。该方法可以被调用者期待获得 SignResponse

 私人任务< SignResponse> GetSignDataAsync(SigningRequestType要求)
{
    返回_service.SignAsync(请求);
}

同样,这个版本做同样的事...的除了异步/的await 不需要关键字​​的。不需要他们的原因是,该方法本身并不需要使用 SignResponse ,因此它可以简单的返回任务< SignResponse> 如上所示。而当你在你的问题指出,的确有罚款,当你使用异步/的await 时是不需要的关键词。这样做增加了额外的状态机步骤,因为其结果是产生了,因为它的期待已久的。

 专用异步任务< SignResponse> GetSignDataAsync(SigningRequestType要求)
{
    返回等待_service.SignAsync(要求).ConfigureAwait(假);
}

最后,如果你需要推理的反应,可以使用前面提到的关键字,这样做是这样的:

 专用异步任务< SignResponse> GetSignDataAsync(SigningRequestType要求)
{
    VAR的结果=等待_service.SignAsync(要求).ConfigureAwait(假);
    如果(result.SomeProperty == SomethingWeCareToCheck)
    {
        _log.Log(哇,这是未预期的...);
    }
    返回结果;
}

Summary: In a library method, when should I use the async and await keywords instead of returning a Task directly?

I believe my question is related to this one. However, that question is about .NET 4.0 and the TPL, while I'm using .NET 4.6 with the async and await keywords. So, I think my question might get different answers because these keywords didn't exist when the linked question was answered.

Explanation: I'm writing a simple wrapper for an external WCF service and the wrapper makes multiple SendAsync calls. Now I think that each wrapper method should just return a Task<> directly without being awaited. My understanding is that async/await should be used on the application layer, and not within a library.

So, for example, here is the approach that I think I should take for each wrapper method:

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

But on the Internet, I found several posts that use this approach instead:

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

And here is another example that I found 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");
}

So, when should I use the second approach (the one that includes the async and await keywords)? Why not just return a whole Task without making PutTaskDelay async? I think that I should return Task directly whenever it is possible, and use async/await to get a final result in the application layer only. Am I right? If not, what is the difference between the two approaches that I show here?

My concern: When the async and await keywords are used, it seems that it just provides additional work to the compiler without any benefit.

Should I use async await in library?

It all depends. If you're going to take advantage of the asynchronous programming paradigm, then the answer is "yes," the async and await keywords are needed most of the time. More than likely, you will find yourself needing to use async/await. That is because in most situations it would be difficult to use only Task and Task<T> as you will more than likely need to reason about the results of the async operations that you invoke.

Additionally, based on your question it seems as though you may have some confusion about the keywords themselves and how they relate to the Task and Task<T> types. Allow me to clarify this for you.

The async keyword allows a method to use the await keyword. The best practice is to have all async methods return either Task or Task<T> unless you are unable to (for example, a button click event handler as you demonstrated above).

Methods that return Task or Task<T> represent asynchronous operations. When you are in a library it is suggested to always use .ConfigureAwait(false) for reasons detailed here. Additionally, I always point people to this detailed article on the subject.

To differentiate the two approaches in your question:

The method below returns a Task<SignResponse>. This is an async operation that represents the work to sign in. The method can be awaited by the caller to get SignResponse.

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

Likewise, this version does the same thing...except that the async/await keywords are not needed. The reason they are not needed is that the method itself does not need to use SignResponse and therefore it could simply return Task<SignResponse> as shown above. And as you indicated in your question, there is indeed a penalty when you use the async/await keywords when they are not needed. Doing so adds an extra state machine step as the result is yielded, since its awaited.

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

Finally, if you needed to reason about the response, you could use the aforementioned keywords to do so like this:

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;
}