首页 > 解决方案 > Excessive use of async await?

问题描述

I'm wondering if I use async and await to excessively in my code and if there are (performance) penalties in doing so?

What I often do:

static void Main()
{
    var result = Task<int>.Run (()=> S1Async(1)).Result;
    Console.WriteLine(result);
}

static async Task<int> WrapperAsync(Func<int, Task<int>> func) => await func(2);
static async Task<int> S1Async(int x) => await WrapperAsync(async t=> await S2Async(x * t));
static async Task<int> S2Async(int x) => await WrapperAsync(async t=> await S3Async(x * t));
static async Task<int> S3Async(int x) => await WrapperAsync(async t=> await S4Async(x * t));
static async Task<int> S4Async(int x) => await Task.FromResult(x * 10);

I think the async-awaits can be skipped and that this code is similar:

static void Main()
{
    var result = Task<int>.Run(() => S1Async(1)).Result;
    Console.WriteLine(result);
}

static Task<int> WrapperAsync(Func<int, Task<int>> func) => func(2);
static Task<int> S1Async(int x) => WrapperAsync(t => S2Async(x * t));
static Task<int> S2Async(int x) => WrapperAsync(t => S3Async(x * t));
static Task<int> S3Async(int x) => WrapperAsync(t => S4Async(x * t));
static Task<int> S4Async(int x) => Task.FromResult(x * 10);

When tasks are nested with just one task per level, is it safe to skip async/await?

Both code-samples gives the same result in LinqPad, so I assume they are similiar, but maybe there are side-effects I should be aware of?

标签: c#.netasync-awaitroslyn

解决方案


简短的回答是:是的,在这种情况下你可以跳过它们,是的,有性能损失。

当您的方法所做的只是await一次并立即返回时,如下所示:

async Task FooAsync()
{
    /* ... */

    await BarAsync();
}

那么它几乎相当于写:

Task FooAsync()
{
    /* ... */

    return BarAsync();
}

的意思async是(从 的角度来看FooAsync):好吧,操作BarAsync可能需要一段时间才能完成,所以如果我们得到一个未完成Task的结果,让我保存我当前的状态并将控制流返回给我的调用者。一旦Task完成,我想恢复状态并继续我一直在做的事情。

在这种情况下,很明显在完成后不会执行任何额外的工作BarAsync Task,因此您可以将其返回Task给调用者,因为有效地FooAsync完成了完全相同的时刻Task。无需保存任何状态或安排继续。

当我们谈论单个调用时,这样做的开销并不大,但是如果您多次调用此方法,您可能会感觉到影响。async编译器必须在您声明您的方法的那一刻设置整个基础架构async- 状态机,继续调度,整个包。所以作为一般规则:如果你可以简化你的方法而不是async简单地返回另一个Task,那么这样做是值得的。

附带说明:您当然不需要将S1Async调用包装到 aTask.Run中,并且您不必在Main自 C# 7.1 以来同步阻塞。你可以写:

static async Task Main()
{
    var result = await S1Async(1);
    Console.WriteLine(result);
}

推荐阅读