立即等待异步任务 vs 先声明然后等待

Nic*_*sas 5 c# asynchronous task async-await

让我们看以下两个例子:

public class MyClass 
{
    public async Task Main() 
    {
        var result1 = "";
        var result2 = "";

        var request1 = await DelayMe();
        var request2 = await DelayMe();

        result1 = request1;
        result2 = request2;        
    }

    private static async Task<String> DelayMe()
    {
        await Task.Delay(2000);
        return "";
    }
}
Run Code Online (Sandbox Code Playgroud)

和:

public class MyClass 
{
    public async Task Main() 
    {
        var result1 = "";
        var result2 = "";

        var request1 = DelayMe();
        var request2 = DelayMe();

        result1 = await request1;
        result2 = await request2;        
    }

    private static async Task<String> DelayMe()
    {
        await Task.Delay(2000);
        return "";
    }
}
Run Code Online (Sandbox Code Playgroud)

在第一个示例中,您通常会如何编写async await代码,其中一件事接一件事发生并正确等待。

第二个是首先调用该async Task方法,但await稍后调用。

第一个示例需要一些时间4000ms来执行,因为await它会在发出第二个请求之前计算第一个请求;但第二个例子需要一点2000ms。发生这种情况是因为Task一旦执行越过该var request1 = DelayMe();行,实际上就开始运行,这意味着request1并且request2正在并行运行。在这一点上,await关键字看起来只是确保Task计算了 。

第二种方法的感觉和行为类似于await Task.WhenAll(request1, request2),但在这种情况下,如果 2 个请求中出现某些问题,您将立即收到异常,而不是等待所有内容计算完毕然后获得AggregateException.

我的问题是,当一种方法的结果不依赖于另一种方法的执行时,使用第二种方法并行运行多个await能力是否存在缺陷(性能或其他)Task?查看降低的代码,看起来第二个示例生成了等量的System.Threading.Tasks.Task1 per awaited item while the first one doesn't. Is this still going through theasync await` 状态机流?

Ste*_*ary 4

如果两个请求中出现某些问题,您将立即收到异常,而不是等待所有内容计算完毕然后收到 AggregateException。

如果第一个请求失败,那么是的。如果第二个请求失败,那么不,在该任务完成之前您不会检查第二个请求结果await

我的问题是,当一个任务的结果不依赖于另一个任务的执行时,使用第二种方法并行运行多个可等待任务是否存在缺点(性能或其他方面)?查看降低的代码,看起来第二个示例生成了等量的 System.Threading.Tasks.Task1per 等待项,而第一个示例则没有。这是否仍在经历 async wait` 状态机流程?

它仍在经历状态机流程。我倾向于推荐,await Task.WhenAll因为代码的意图更明确,但有些人不喜欢“即使有异常也总是等待”的行为。另一方面是Task.WhenAll总是收集所有异常 - 如果您有快速失败行为,那么某些异常可能会被忽略。

关于性能,并发执行会更好,因为您可以同时执行多个操作。不存在线程池耗尽的危险,因为async/await不使用额外的线程。

作为旁注,我建议使用术语“异步并发”而不是“并行”,因为对许多人来说“并行”意味着并行处理,即ParallelPLINQ,在这种情况下使用这将是错误的技术。