等待一组任务的最理想方法是什么?

时间:2019-06-14 15:59:28

标签: c# .net performance asynchronous async-await

我正在探索async / await,并且发现了一个奇怪的场景,需要解决方案方面的指导。

作为参考,可以在这里找到此问题中看到的代码:

https://github.com/Mike-EEE/Stash/tree/master/AwaitPerformance

我提供了两种等待一组任务的简单方法。首先是简单地创建一个List<Task>,将任务添加到此列表中,并通过调用Task.WhenAll立即等待整个结果:

public async Task<uint> AwaitList()
{
    var list = new List<Task>();

    for (var i = 0u; i < 10; i++)
    {
        list.Add(Task.Delay(1));
    }

    await Task.WhenAll(list).ConfigureAwait(false);

    return 123;
}

第二个方法是等待每个任务在for循环中发生:

public async Task<uint> AwaitEach()
{
    for (var i = 0u; i < 10; i++)
    {
        await Task.Delay(1).ConfigureAwait(false);
    }

    return 123;
}

但是,当使用Benchmark.NET运行这两种方法时,我得到的结果却令人惊讶地矛盾:

// * Summary *

BenchmarkDotNet=v0.11.5, OS=Windows 10.0.18362
AMD Ryzen 7 2700X, 1 CPU, 8 logical and 4 physical cores
.NET Core SDK=3.0.100-preview5-011568
  [Host]     : .NET Core 3.0.0-preview5-27626-15 (CoreCLR 4.6.27622.75, CoreFX 4.700.19.22408), 64bit RyuJIT
  DefaultJob : .NET Core 3.0.0-preview5-27626-15 (CoreCLR 4.6.27622.75, CoreFX 4.700.19.22408), 64bit RyuJIT


|    Method |      Mean |     Error |    StdDev | Gen 0 | Gen 1 | Gen 2 | Allocated |
|---------- |----------:|----------:|----------:|------:|------:|------:|----------:|
| AwaitList |  15.60 ms | 0.0274 ms | 0.0243 ms |     - |     - |     - |    2416 B |
| AwaitEach | 155.62 ms | 0.9113 ms | 0.8524 ms |     - |     - |     - |     352 B |

如您所见,等待任务列表要快得多,但是会产生大量分配。但是,等待每一项都是相反的:它速度较慢,但​​产生的垃圾少。

我是否有一种显而易见的理想方法来让这两个世界都处于最佳境地?也就是说,是否有一种方法可以等待一组Task元素,这些元素既快速又导致分配量低?

在此先感谢您的帮助。

1 个答案:

答案 0 :(得分:4)

您不是要在这里比较苹果。

在您的示例中:

  • AwaitList创建一个任务列表,然后并行(异步)运行它们。

  • AwaitEach依次运行每个Task,因此使async关键字无用。


但是,如果您列出了任务列表,以便可以启动每个任务,然后比较WhenAll与循环,则您的比较将如下所示:

public async Task<uint> AwaitList()
{
    var list = new List<Task>();

    for (var i = 0u; i < 10; i++)
    {
        list.Add(Task.Delay(1));
    }

    await Task.WhenAll(list).ConfigureAwait(false);

    return 123;
}

经文

public async Task<uint> AwaitEach()
{
    var list = new List<Task>();

    for (var i = 0; i < 10; i++)
    {
        list.Add(Task.Delay(1));
    }

    for (var i = 0; i < 10; i++)
    {
        await list[i].ConfigureAwait(false);
    }
    return 123;
}

现在比较这两个功能的统计信息,您会发现它们是彼此的基石。