采用 DRY 和分而治之原则的异步方法链接



假设我们有以下方法:

private async Task<string> Foo(string parameter)
{
    // Some code to convert source parameter
    string convertedParameter = //value;
    CallResult callResult;
    try
    {
        var integerResult = await LoadInformationAsync(convertedParameter);
        if (integerResult > 0)
        {
            callResult = // Some logic to analyse integerResult and generate CallResult object
        }
        else
        {
            callResult = // Some logic to analyse integerResult and generate CallResult object
        }
    }
    catch (Exception ex)
    {
        callResult = CallResult.Default; // some default value if call failed
    }
    var stringResult = // some logic to convert callResult instance to some another string result;
    return stringResult; //Finally return the result
}

我们不要深入讨论细节。主要的是这个方法包含一些业务逻辑和调用(假设是3d方)方法LoadInformationAsync,这是可等待的。

在这些注释后面可能是大量的业务逻辑,所以,我想,每个人都会同意将逻辑拆分为单独的方法(甚至类)绝对是好的。

因此,LoadInformationAsync方法的核心调用将深入到调用堆栈中。像这样:

private async Task<string> Foo(string parameter)
{
    // Some code to convert source parameter
    string convertedParameter = //value;
    CallResult callResult = await MakeSafeCall(convertedParameter);
    var stringResult = // some logic to convert callResult instance to some another string result;
    return stringResult; //Finally return the result
}
private async Task<CallResult> MakeSafeCall(string parameter)
{
    try
    {
        var integerResult = await LoadInformationAsync(convertedParameter);
        if (integerResult > 0)
        {
            return callResult = // Some logic to analyse integerResult and generate CallResult object
        }
        else
        {
            return callResult = // Some logic to analyse integerResult and generate CallResult object
        }
    }
    catch (Exception ex)
    {
        return CallResult.Default;
    }
}

的结果是我们的代码稍微好一些。F.e.是一些类/方法可能想要调用方法MakeSafeCall有那里的try/catch。

但是我们现在有什么?我们有一个额外的异步方法需要等待。每一对async/await都带来一个捕获上下文的状态机,等等。好的,我们可以处理这个开销,但是如果我们有更复杂的逻辑(我们经常会遇到这种情况)迫使我们把根方法分割成更小的和平呢?我们的async/await对计数将会增加。而且看起来不太好。

那么问题来了:在这种情况下使用async/await的好模式是什么?

我认为Stephan Toub在他的文章Async性能:理解Async和Await的成本

中对你的问题给出了很好的答案

异步方法是一个强大的生产力工具,使您能够更容易编写可伸缩和响应的库和应用程序。但是,重要的是要记住,异步性不是单个操作的性能优化。取a同步操作和将其变为异步操作将不可避免地降低该操作的性能,因为它仍然需要这样做完成同步操作所做的一切,但现在使用其他约束和注意事项。一个你关心的理由那么,异步性是性能的总和:如何当您异步写入所有内容时,整个系统将执行可以重叠I/O并实现更好的系统利用率只在真正需要的时候才消耗有价值的资源执行。.NET提供的异步方法实现框架得到了很好的优化,并且通常最终提供了很好的或很好的服务性能优于编写良好的异步实现使用现有模式并卷取更多代码。任何时候你计划从现在开始在。net框架中开发异步代码因此,异步方法应该是您选择的工具。

底线是,不要过早优化。如果您对代码进行了基准测试,发现async方法是一个瓶颈,那么请查看它在背后做了什么,以及如何编写更面向性能的代码。但是请记住,. net框架团队在实现async-await时考虑到了这一点,您可以在细节中看到这一点,例如使AsyncTaskMethodBuilder成为一个结构体而不是一个类以减少GC压力,等等。

我建议您仔细阅读stephans的文章,以更好地理解框架所做的成本/优化。

开销实际上可以忽略不计,特别是当它被大量业务逻辑包围时。当有多余的async-await调用时,您可以尝试删除它们:

:

async Task Wait()
{
    await Task.Delay(1000);
}

:

Task Wait()
{
    return Task.Delay(1000);
}

但不要纠结于此。你可能永远都不会到达瓶颈点。如果你这样做了,还有更多的。net特性需要删除,比如yield(和大部分LINQ),甚至lambda表达式。

最新更新