WhenAny vs WhenAll vs WaitAll vs none, учитывая, что результаты используются немедленно - PullRequest
3 голосов
/ 12 марта 2020

Я должен использовать вывод нескольких асинхронных задач сразу после их завершения.

Будет ли разумная разница в любом из этих подходов?

Простое ожидание

public async Task<List<Baz>> MyFunctionAsync(List<Foo> FooList) {
    results = new List<Baz>();
    List<Task<List<Baz>>> tasks = new List<Task<List<Baz>>>();

    foreach (Foo foo in FooList) {
        tasks.Add(FetchBazListFromFoo(entry));

    foreach (Task<List<Baz>> task in tasks) {
        results.AddRange(await task);

    return results;
}

WhenAll

public async Task<List<Baz>> MyFunctionAsync(List<Foo> FooList) {
    results = new List<Baz>();
    List<Task<List<Baz>>> tasks = new List<Task<List<Baz>>>();

    foreach (Foo foo in FooList) {
        tasks.Add(FetchBazListFromFoo(entry));

    foreach (List<Baz> bazList in await Task.WhenAll(tasks))
        results.AddRange(bazList);

    return results;
}

WaitAll

public async Task<List<Baz>> MyFunctionAsync(List<Foo> FooList) {
    results = new List<Baz>();
    List<Task<List<Baz>>> tasks = new List<Task<List<Baz>>>();

    foreach (Foo foo in FooList) {
        tasks.Add(FetchBazListFromFoo(entry));

    foreach (List<Baz> bazList in await Task.WaitAll(tasks))
        results.AddRange(bazList);

    return results;
}

WhenAny

public async Task<List<Baz>> MyFunctionAsync(List<Foo> FooList) {
    results = new List<Baz>();
    List<Task<List<Baz>>> tasks = new List<Task<List<Baz>>>();

    foreach (Foo foo in FooList) {
        tasks.Add(FetchBazListFromFoo(entry));

    while (tasks.Count > 0) {
        Task<List<Baz>> finished = Task.WhenAny(tasks);
        results.AddRange(await finished);
        tasks.Remove(finished);
    }

    return results;
}
  • FooList имеет около 100 записей.
  • FetchBazListFromFoo делает около 30 вызовов API REST и выполняет некоторую синхронную работу для каждого результата вызова API REST.

Кроме того, существует ли внутренняя разность служебных данных в WhenAll v WhenAny?

WhenAll возвращает управление после завершения всех задач, а WhenAny возвращает управление, как только одна задача завершена. Последнее, похоже, требует большего внутреннего управления.

Ответы [ 2 ]

3 голосов
/ 13 марта 2020

Третий подход (WaitAll) недопустим, потому что Task.WaitAll является методом возврата void, поэтому его нельзя ожидать. Этот код просто выдаст ошибку во время компиляции.

Три других подхода очень похожи, с некоторыми незначительными различиями.

Простое ожидание: запускает все задачи и затем ждет их один за другим. Он соберет все результаты в правильном порядке. В случае исключения будет возвращено до того, как все задачи будут завершены, и будет сообщать только об исключении первой неудачной задачи (сначала по порядку, а не в хронологическом порядке).
Не рекомендуется, если это поведение именно то, что вы хотите (скорее всего, это не).

WhenAll : запускает все задачи, а затем ожидает их выполнения. Он соберет все результаты в правильном порядке. В случае исключения будет возвращено после того, как все задачи были выполнены, и будет сообщать только об исключении из первой неудачной задачи (сначала по порядку, а не в хронологическом порядке).
Не рекомендуется, если это поведение именно то, что вы хотите (наиболее вероятно это не так).

WhenAny : запускает все задачи, а затем ожидает их выполнения. Все результаты будут собраны в порядке их завершения, поэтому исходный порядок не будет сохранен. В случае возникновения исключения немедленно вернутся и сообщат об исключении первой неудачной задачи (на этот раз в хронологическом порядке, а не по порядку). В while l oop вводятся накладные расходы, которые отсутствуют в двух других подходах, которые будут весьма значительными, если число задач превышает 10 000, и будут расти в геометрической прогрессии по мере увеличения числа задач.
Не рекомендуется, если это не то поведение, которое вам нужно (держу пари, что вы не должны быть его поклонниками).

Все эти подходы : бомбардирует удаленный сервер с помощью огромное количество одновременных запросов, затрудняющих быстродействие этой машины и в худшем случае запускающих защитный механизм анти-DOS-атаки.

Лучшее решение этой проблемы - отбросить все эти посредственные подходы, и вместо этого использовать библиотеку, специализированную для такого рода проблем, которая встроена в. NET Core и доступна в виде пакета для. NET Framework. Это библиотека TPL Dataflow , которая позволяет построить конвейер обработки, состоящий из блоков обработки syn c или asyn c, каждый из которых сконфигурирован со своей степенью параллелизма. Вы получите оптимальную производительность, вы получите контроль над тем, какую нагрузку вы поместите на удаленный сервер, вы будете оптимально использовать ресурсы ЦП вашего локального компьютера, вы получите результаты в правильном порядке, и вы ' Я получу исключение вскоре после провала первой задачи. В качестве бонуса вы также получаете возможность отменить операцию в любое время, если у вас есть sh. Недостатком является кривая обучения, которая, к счастью, не очень крутая. Вы сможете успешно использовать эту библиотеку после одного или двух дней обучения.

0 голосов
/ 12 марта 2020

Простое ожидание будет выполнять каждый элемент один за другим, по существу, синхронно - это будет самым медленным.

WhenAll будет ожидать выполнения всех задач - время выполнения будет любым самым длинным задача.

Не используйте WaitAll - это синхронно, просто используйте WhenAll

WhenAny позволяет вам обрабатывать каждую задачу по мере ее завершения. В некоторых случаях это будет быстрее, чем WhenAll, в зависимости от того, сколько обработки вы должны выполнить после выполнения задачи.

IMO, если только вам не нужно запускать постобработку сразу после завершения каждой задачи, WhenAll - это самый простой / самый чистый подход, который будет работать нормально в большинстве сценариев ios.

...