Какой самый идеальный способ дождаться выполнения множества задач? - PullRequest
0 голосов
/ 14 июня 2019

Я изучаю 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 Ответ

4 голосов
/ 14 июня 2019

Вы не сравниваете яблоки с яблоками здесь.

В вашем примере:

  • AwaitList создает список задач и затем запускает ихвсе параллельные (асинхронные).

  • AwaitEach запускает каждую задачу одну за другой, поэтому ключевое слово async становится бесполезным.


Если, однако,, вы составляете свой список задач, так что каждая задача может запускаться, а затем сравнивать Когда все против цикла, ваше сравнение будет выглядеть так:

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;
}

verses

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;
}

Теперь сравнитестатистику по этим двум функциям, и вы увидите, что они являются приблизительными.

...