Какова правильная реализация Task.WhenAll () для этого сценария? [Дубликат]

Во-первых, вы не можете иметь несколько элементов с одним и тем же идентификатором. Я знаю, что вы сказали, что не можете контролировать, как создается форма, но ... попытайтесь как-то удалить все идентификаторы из радиостанций или сделать их уникальными.

Чтобы получить значение выбранного радио , выберите его по имени с фильтром :checked.

var selectedVal = "";
var selected = $("input[type='radio'][name='s_2_1_6_0']:checked");
if (selected.length > 0) {
    selectedVal = selected.val();
}

EDIT

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

var selectedVal = "";
var selected = $("#radioDiv input[type='radio']:checked");
if (selected.length > 0) {
    selectedVal = selected.val();
}
133
задан abatishchev 18 May 2016 в 07:00
поделиться

9 ответов

После использования WhenAll вы можете вывести результаты по отдельности с помощью await:

var catTask = FeedCat();
var houseTask = SellHouse();
var carTask = BuyCar();

await Task.WhenAll(catTask, houseTask, carTask);

var cat = await catTask;
var house = await houseTask;
var car = await carTask;

Вы также можете использовать Task.Result (поскольку вы знаете, что к этому моменту все они завершены успешно). Однако я рекомендую использовать await, потому что это явно правильно, в то время как Result может вызвать проблемы в других сценариях.

224
ответ дан Servy 16 August 2018 в 04:46
поделиться
  • 1
    Вы можете просто удалить WhenAll из этого полностью; ожидающие позаботятся о том, чтобы вы не проходили мимо 3 последующих заданий, пока все задачи не будут завершены. – Servy 19 June 2013 в 18:45
  • 2
    Task.WhenAll() позволяет запустить задачу в режиме parallel . Я не понимаю, почему @Servy предложил его удалить. Без WhenAll они будут запускаться один за другим – Sergey 13 March 2015 в 13:07
  • 3
    @Sergey: задачи начинают выполняться немедленно. Например, catTask уже работает к моменту его возврата из FeedCat. Таким образом, любой из подходов будет работать - единственный вопрос заключается в том, хотите ли вы await по одному за раз или все вместе. Обработка ошибок несколько отличается - если вы используете Task.WhenAll, то все await будут все, даже если один из них не работает раньше. – Stephen Cleary 13 March 2015 в 13:17
  • 4
    @Sergey Calling WhenAll не влияет на выполнение операций или их выполнение. Только имеет любую возможность влияния на результаты. В этом конкретном случае единственное различие заключается в том, что ошибка в одном из первых двух методов привела бы к тому, что исключение было бы выбрано в этом стеке вызовов ранее в моем методе, чем у Стивена (хотя такая же ошибка всегда была бы выбрана, если есть ). – Servy 16 March 2015 в 15:05
  • 5
    @Sergey: ключ заключается в том, что асинхронные методы всегда возвращают "hot" (уже начатых) задач. – Stephen Cleary 17 March 2015 в 20:02

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

WaitAll vs WhenAll

2
ответ дан Community 16 August 2018 в 04:46
поделиться

Используйте Task.WhenAll, а затем ждите результатов:

var tCat = FeedCat();
var tHouse = SellHouse();
var tCar = BuyCar();
await Task.WhenAll(tCat, tHouse, tCar);
Cat cat = await tCat;
House house = await tHouse;
Tesla car = await tCar; 
//as they have all definitely finished, you could also use Task.Value.
2
ответ дан It'sNotALie. 16 August 2018 в 04:46
поделиться
  • 1
    mm ... not Task.Value (возможно, он существовал в 2013 году?), скорее tCat.Result, tHouse.Result или tCar.Result – Stephen York 27 October 2017 в 00:06

Если вы используете C # 7, вы можете использовать удобный метод обертки, подобный этому ...

public static class TaskEx
{
    public static async Task<(T1, T2)> WhenAll<T1, T2>(Task<T1> task1, Task<T2> task2)
    {
        await Task.WhenAll(task1, task2);
        return (task1.Result, task2.Result);
    }
}

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

var (someInt, someString) = await TaskEx.WhenAll(GetIntAsync(), GetStringAsync());
19
ответ дан Joel Mueller 16 August 2018 в 04:46
поделиться
  • 1
    Я не могу найти эту функцию в финальной версии ... – Yury Shcherbakov 25 March 2017 в 14:57
  • 2
    Кортежи - единственная функция C # 7. Это определенно в финальном выпуске. – Joel Mueller 8 April 2017 в 23:23
  • 3
    Я знаю о кортежах и c # 7. Я имею в виду, что не могу найти метод WhenAll, который возвращает кортежи. Какое пространство имен / пакет? – Yury Shcherbakov 10 April 2017 в 10:10
  • 4
    @YuryShcherbakov Task.WhenAll() не возвращает кортеж. Один из них создается из свойств Result предоставленных задач после завершения задачи, возвращаемой Task.WhenAll(). – Chris Charabaruk 18 August 2017 в 23:20

Учитывая три задачи: FeedCat(), SellHouse() и BuyCar(), есть два интересных случая: либо они все завершаются синхронно (по какой-то причине, возможно, кэширование или ошибка), либо они не делают.

Скажем, у нас есть вопрос:

Task<string> DoTheThings() {
    Task<Cat> x = FeedCat();
    Task<House> y = FeedCat();
    Task<Tesla> z = FeedCat();
    // what here?
}

. Теперь простой подход будет:

Task.WhenAll(x, y, z);

, но ... это не удобный для обработки результатов; мы обычно хотели бы await, что:

async Task<string> DoTheThings() {
    Task<Cat> x = FeedCat();
    Task<House> y = FeedCat();
    Task<Tesla> z = FeedCat();

    await Task.WhenAll(x, y, z);
    // presumably we want to do something with the results...
    return DoWhatever(x.Result, y.Result, z.Result);
}

, но это делает много накладных расходов и выделяет различные массивы (включая массив params Task[]) и списки (внутренне). Это работает, но это не очень хорошая ИМО. Во многих отношениях проще использовать операцию async и только await каждый по очереди:

async Task<string> DoTheThings() {
    Task<Cat> x = FeedCat();
    Task<House> y = FeedCat();
    Task<Tesla> z = FeedCat();

    // do something with the results...
    return DoWhatever(await x, await y, await z);
}

Вопреки некоторым комментариям выше, использование await вместо Task.WhenAll делает не отличается от того, как выполняются задачи (одновременно, последовательно и т. д.). На самом высоком уровне Task.WhenAll предшествует хорошей поддержке компилятора для async / await и полезен , когда эти вещи не существовали . Это также полезно, когда у вас есть произвольный массив задач, а не 3 сдержанных задач.

Но: у нас все еще есть проблема, что async / await генерирует много шума компилятора для продолжения , Если вероятность того, что задачи действительно завершится синхронно, , мы можем оптимизировать это, построив синхронный путь с асинхронным резервным копированием:

Task<string> DoTheThings() {
    Task<Cat> x = FeedCat();
    Task<House> y = FeedCat();
    Task<Tesla> z = FeedCat();

    if(x.Status == TaskStatus.RanToCompletion &&
       y.Status == TaskStatus.RanToCompletion &&
       z.Status == TaskStatus.RanToCompletion)
        return Task.FromResult(
          DoWhatever(a.Result, b.Result, c.Result));
       // we can safely access .Result, as they are known
       // to be ran-to-completion

    return Awaited(x, y, z);
}

async Task Awaited(Task<Cat> a, Task<House> b, Task<Tesla> c) {
    return DoWhatever(await x, await y, await z);
}

Этот «путь синхронизации с async fallback "становится все более распространенным, особенно в высокопроизводительном коде, где синхронные завершения относительно часты. Обратите внимание, что это вообще не поможет, если завершение всегда истинно асинхронно.

Дополнительные действия, которые применяются здесь:

1: с недавним C #, общий шаблон для async метод резервного копирования обычно реализуется как локальная функция:

Task<string> DoTheThings() {
    async Task<string> Awaited(Task<Cat> a, Task<House> b, Task<Tesla> c) {
        return DoWhatever(await a, await b, await c);
    }
    Task<Cat> x = FeedCat();
    Task<House> y = FeedCat();
    Task<Tesla> z = FeedCat();

    if(x.Status == TaskStatus.RanToCompletion &&
       y.Status == TaskStatus.RanToCompletion &&
       z.Status == TaskStatus.RanToCompletion)
        return Task.FromResult(
          DoWhatever(a.Result, b.Result, c.Result));
       // we can safely access .Result, as they are known
       // to be ran-to-completion

    return Awaited(x, y, z);
}

2: предпочитает ValueTask<T> - Task<T>, если есть хорошие шансы на вещи когда-либо полностью синхронно со многими различными значениями возврата:

ValueTask<string> DoTheThings() {
    async ValueTask<string> Awaited(ValueTask<Cat> a, Task<House> b, Task<Tesla> c) {
        return DoWhatever(await a, await b, await c);
    }
    ValueTask<Cat> x = FeedCat();
    ValueTask<House> y = FeedCat();
    ValueTask<Tesla> z = FeedCat();

    if(x.IsCompletedSuccessfully &&
       y.IsCompletedSuccessfully &&
       z.IsCompletedSuccessfully)
        return new ValueTask<string>(
          DoWhatever(a.Result, b.Result, c.Result));
       // we can safely access .Result, as they are known
       // to be ran-to-completion

    return Awaited(x, y, z);
}

3: если возможно, предпочитайте IsCompletedSuccessfully - Status == TaskStatus.RanToCompletion; это теперь существует в .NET Core для Task и везде для ValueTask<T>

3
ответ дан Marc Gravell 16 August 2018 в 04:46
поделиться
  • 1
    «В отличие от различных ответов здесь, используйте wait, а не Task.WhenAll не имеет никакого значения, как выполняются задачи (одновременно, последовательно и т. д.) & quot; Я не вижу ответа, который говорит это. Я бы уже прокомментировал их, сказав столько, если они это сделали. Есть много комментариев по множеству ответов, говорящих об этом, но ответов нет. На что вы ссылаетесь? Также обратите внимание, что ваш ответ не обрабатывает результат задач (или имеет дело с тем, что результаты относятся к другому типу). Вы создали их в методе, который просто возвращает Task, когда все делается без использования результатов. – Servy 6 February 2018 в 17:24
  • 2
    @Servy, вы правы, это были комментарии; Я добавлю трюк, чтобы показать, используя результаты – Marc Gravell♦ 6 February 2018 в 17:28
  • 3
    @Servy tweak добавлен – Marc Gravell♦ 6 February 2018 в 17:33
  • 4
    Кроме того, если вы собираетесь отказаться от выполнения синхронных задач, вы можете обрабатывать любые задачи, которые синхронно отменены или сработаны, а не только успешно завершенные. Если вы приняли решение, что это оптимизация, которую требует ваша программа (что будет редко, но произойдет), вы также можете пройти весь путь. – Servy 6 February 2018 в 17:34
  • 5
    @Servy - это сложная тема - вы получаете другую семантику исключений из двух сценариев - ожидание инициирования исключения ведет себя иначе, чем доступ к .Result для запуска исключения. ИМО в этот момент мы должны await получить «лучше». исключая семантику, исходя из предположения, что исключения являются редкими, но значимыми – Marc Gravell♦ 6 February 2018 в 17:37

Вы можете сохранить их в задачах, а затем ждать их всех:

var catTask = FeedCat();
var houseTask = SellHouse();
var carTask = BuyCar();

await Task.WhenAll(catTask, houseTask, carTask);

Cat cat = await catTask;
House house = await houseTask;
Car car = await carTask;
7
ответ дан Reed Copsey 16 August 2018 в 04:46
поделиться
  • 1
    не выполняет var catTask = FeedCat() выполнение функции FeedCat() и сохраняет результат в catTask, делая часть await Task.WhenAll() части бесполезной, поскольку метод уже выполнен? – Kraang Prime 30 July 2016 в 23:09
  • 2
    @sanuel, если они возвращают задачу & lt; t & gt ;, тогда нет ... они запускают async open, но не ждут его – Reed Copsey 30 July 2016 в 23:25
  • 3
    Я не думаю, что это точно, пожалуйста, см. Обсуждения в соответствии с ответом @ StephenCleary ... также см. Ответ Servy. – Rosdi Kasim 10 January 2017 в 05:37
  • 4
    если мне нужно добавить .ConfigrtueAwait (false). Добавлю ли я его только в Task.WhenAll или в каждый следующий awaiter? – AstroSharp 24 February 2017 в 19:19
  • 5
    @AstroSharp в целом, это хорошая идея, чтобы добавить его ко всем из них (если первое завершено, оно эффективно игнорируется), но в этом случае, вероятно, было бы хорошо сделать первый - если не будет асинхронного что происходит позже. – Reed Copsey 24 February 2017 в 20:05

Просто await три задачи отдельно, после их запуска.

var catTask = FeedCat();
var houseTask = SellHouse();
var carTask = BuyCar();

var cat = await catTask;
var house = await houseTask;
var car = await carTask;
55
ответ дан Rosdi Kasim 16 August 2018 в 04:46
поделиться
  • 1
    Я не думаю, что это также возвращает результаты ... – It'sNotALie. 19 June 2013 в 18:42
  • 2
    Возвращаемые значения различны, поэтому «новый []» не будет работать – BahaiResearch.com 19 June 2013 в 18:42
  • 3
    @Bargitta Нет, это неверно. Они будут выполнять свою работу параллельно. Не стесняйтесь запускать его и видеть сами. – Servy 11 October 2016 в 13:00
  • 4
    Люди продолжают задавать один и тот же вопрос после нескольких лет ... Я чувствую, что важно снова подчеркнуть, что задача & quot; начинается с создания & quot; в теле ответа : возможно, они не удосуживаются читать комментарии – user 17 May 2017 в 07:24
  • 5
    @StephenYork Добавление Task.WhenAll практически ничего не меняет о поведении программы любым наблюдаемым способом. Это чисто избыточный вызов метода. Вы можете добавить его, если хотите, в качестве эстетического выбора, но это не меняет того, что делает код. Время выполнения кода будет идентичным с вызовом этого метода или без него (ну, технически будет накладные расходы действительно малые для вызова WhenAll, но это должно быть незначительным), только делая это версии слегка дольше, чем эта версия. – Servy 25 October 2017 в 21:22

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

var catTask = FeedCat();
var houseTask = SellHouse();
var carTask = BuyCar();

await Task.WhenAll(catTask, houseTask, carTask);

var cat = await catTask;
var house = await houseTask;
var car = await carTask;

Представьте, что FeedCat генерирует исключение в следующем коде:

var catTask = FeedCat();
var houseTask = SellHouse();
var carTask = BuyCar();

var cat = await catTask;
var house = await houseTask;
var car = await carTask;

В этом случае вы никогда не будете ждать houseTask или carTask. Существует 3 возможных сценария:

  1. SellHouse уже успешно завершен, когда FeedCat не удалось. В этом случае вы в порядке.
  2. SellHouse не является полным и в какой-то момент не работает с ошибкой. Исключение не наблюдается и будет возвращено в финализацию.
  3. SellHouse не является полным и содержит в нем ожидания. В случае, если ваш код работает в ASP.NET, SellHouse завершится с ошибкой, как только некоторые из ожиданий будут завершены внутри него. Это происходит потому, что вы в основном сделали огонь и amp;

Вот ошибка, которую вы получите для случая (3):

System.AggregateException: A Task's exception(s) were not observed either by Waiting on the Task or accessing its Exception property. As a result, the unobserved exception was rethrown by the finalizer thread. ---> System.NullReferenceException: Object reference not set to an instance of an object.
   at System.Web.ThreadContext.AssociateWithCurrentThread(Boolean setImpersonationContext)
   at System.Web.HttpApplication.OnThreadEnterPrivate(Boolean setImpersonationContext)
   at System.Web.HttpApplication.System.Web.Util.ISyncContext.Enter()
   at System.Web.Util.SynchronizationHelper.SafeWrapCallback(Action action)
   at System.Threading.Tasks.Task.Execute()
   --- End of inner exception stack trace ---
---> (Inner Exception #0) System.NullReferenceException: Object reference not set to an instance of an object.
   at System.Web.ThreadContext.AssociateWithCurrentThread(Boolean setImpersonationContext)
   at System.Web.HttpApplication.OnThreadEnterPrivate(Boolean setImpersonationContext)
   at System.Web.HttpApplication.System.Web.Util.ISyncContext.Enter()
   at System.Web.Util.SynchronizationHelper.SafeWrapCallback(Action action)
   at System.Threading.Tasks.Task.Execute()<---

Для случая ( 2) вы получите аналогичную ошибку, но с исходной трассировкой стека исключений.

Для .NET 4.0 и более поздних версий вы можете поймать ненаблюдаемые исключения, используя TaskScheduler.UnobservedTaskException. Для .NET 4.5 и более поздних ненаблюдаемых исключений по умолчанию пропущено исключение unobserved .NET 4.0 приведет к сбою вашего процесса.

Подробнее здесь: Обработка исключений задач в .NET 4.5

1
ответ дан samfromlv 16 August 2018 в 04:46
поделиться

Forward Warning

Просто быстрый хэдшоп для тех, кто посещает этот и другие подобные темы, ищет способ распараллеливать EntityFramework с помощью async + await + task tool-set: Образец, показанный здесь, звучит, однако, когда дело доходит до специальной снежинки EF, вы не достигнете параллельного выполнения, если только до тех пор, пока вы не будете использовать отдельный (новый) db-context-экземпляр внутри каждого вызова * Async ().

Этот вид что необходимо из-за присущих дизайну ограничений ef-db-контекстов, которые запрещают одновременное выполнение нескольких запросов в одном экземпляре ef-db-context.


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

    var car = (Car) null;
    var cat = (Cat) null;
    var house = (House) null;
    using (var carTask = BuyCarAsync())
    using (var catTask = FeedCatAsync())
    using (var houseTask = SellHouseAsync())
    {
        try {
              await Task.WhenAll(carTask, catTask, houseTask);
        } finally {
              car = carTask.Status == TaskStatus.RanToCompletion ? await carTask : null;
              cat = catTask.Status == TaskStatus.RanToCompletion ? await catTask : null;
              house = houseTask.Status == TaskStatus.RanToCompletion ? await houseTask : null;
        }

        if (cat == null || car == null || house == null)
            throw new SomethingFishyGoingOnHereException("...");
   }

Совокупное исключение, которое содержит одно или несколько вспомогательных исключений, будет по-прежнему быть брошенным в конце.

Это зависит от вызывающей среды.

0
ответ дан xDisruptor 16 August 2018 в 04:46
поделиться
Другие вопросы по тегам:

Похожие вопросы: