异步方法与DRY和Divide& Conquer原则相结合

时间:2014-08-19 14:51:23

标签: c# .net async-await dry divide-and-conquer

假设我们有以下方法:

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来尝试/ catch。

但我们现在有什么?我们有一个额外的异步方法需要等待。并且每对async / await都会为捕获上下文的状态机等提供支持。好吧,我们可以处理这个开销,但是如果我们有更复杂的逻辑(通常我们真的这么做)会迫使我们将根方法分成更小的和平。我们的异步/等待对数将增加。而且似乎不是很好。

所以问题是:在这种情况下使用async / await的好方法是什么?

2 个答案:

答案 0 :(得分:3)

我认为Stephan Toub在他的文章 Async Performance: Understanding the Costs of Async and Await

中对你的问题有一个很好的答案
  

异步方法是一种强大的生产力工具,使您能够   更轻松地编写可扩展和响应的库和应用程序。   但重要的是要记住,异步性不是一个   单个操作的性能优化。   同步操作并使其异步将始终如一   降低一个操作的性能,因为它仍然需要   完成同步操作所做的一切,但现在用   其他限制和考虑因素。你关心的一个原因   那么,异步性就是在总体上的表现:你的如何   当您以异步方式编写所有内容时,整个系统会执行   您可以重叠I / O并实现更好的系统利用率   仅在实际需要时才消耗宝贵的资源   执行。 .NET提供的异步方法实现   框架已经过很好的优化,并且通常最终会提供良好的或   比编写良好的异步实现更好的性能   使用现有模式和卷更多代码。任何时候你   计划从现在开始在.NET Framework中开发异步代码   在,异步方法应该是您的首选工具。

最重要的是,不要过早优化。如果您对代码进行基准测试并发现async方法是一个瓶颈,请查看它背后的内容以及如何编写代码以提高性能。但请记住,.NET框架团队在实现async-await时会考虑到这一点,您可以在详细信息中看到,例如使AsyncTaskMethodBuilder成为结构而不是减少GC压力的类等。

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

答案 1 :(得分:1)

开销实际上可以忽略不计,特别是当它被很多业务逻辑包围时。当有以下内容时,您可以尝试删除多余的async-await来电:

转到:

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

要:

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

但不要对此感到困惑。你可能永远不会达到实际上是你的瓶颈的地步。如果你这样做,还有更多的.Net功能需要用,例如yield(以及大多数带有它的LINQ)或者甚至是lambda表达式。