Мне нужно изменить существующую программу, и она содержит следующий код:
var inputs = events.Select(async ev => await ProcessEventAsync(ev))
.Select(t => t.Result)
.Where(i => i != null)
.ToList();
Но это кажется мне очень странным, в первую очередь использование async
и await
в select. Согласно этому ответу Стивена Клири, я смогу отбросить их.
Затем второй, Select
который выбирает результат. Не означает ли это, что задача вообще не асинхронная и выполняется синхронно (столько усилий ни за что), или задача будет выполняться асинхронно, и когда она будет выполнена, остальная часть запроса будет выполнена?
Должен ли я написать приведенный выше код следующим образом в соответствии с другим ответом Стивена Клири :
var tasks = await Task.WhenAll(events.Select(ev => ProcessEventAsync(ev)));
var inputs = tasks.Where(result => result != null).ToList();
и это абсолютно так же, как это?
var inputs = (await Task.WhenAll(events.Select(ev => ProcessEventAsync(ev))))
.Where(result => result != null).ToList();
Пока я работаю над этим проектом, я хотел бы изменить первый пример кода, но я не слишком заинтересован в изменении (явно работающем) асинхронного кода. Может быть, я просто беспокоюсь, и все 3 примера кода делают одно и то же?
ProcessEventsAsync выглядит так:
async Task<InputResult> ProcessEventAsync(InputEvent ev) {...}
источник
Task<InputResult>
сInputResult
того , чтобы быть пользовательский класс.Select
результаты заданий перед вашимиWhere
.Result
свойству задачиОтветы:
Звонок
Select
действителен. Эти две строки практически идентичны:(Существует небольшая разница в том, как генерируется синхронное исключение
ProcessEventAsync
, но в контексте этого кода это не имеет значения).Это означает, что запрос блокируется. Так что это не совсем асинхронно.
Разбивая это:
сначала запустит асинхронную операцию для каждого события. Тогда эта строка:
будет ожидать выполнения этих операций по одной (сначала ожидает операцию первого события, затем следующую, затем следующую и т. д.).
Это та часть, которая меня не волнует, потому что она блокирует, а также оборачивает любые исключения
AggregateException
.Да, эти два примера эквивалентны. Они оба запускают все асинхронные операции (
events.Select(...)
), затем асинхронно ждут завершения всех операций в любом порядке (await Task.WhenAll(...)
), а затем приступают к остальной части работы (Where...
).Оба эти примера отличаются от исходного кода. Исходный код блокирует и будет заключать исключения в
AggregateException
.источник
AggregateException
я получу несколько отдельных исключений во втором коде?Result
этим было бы завернутоAggregateException
.stuff.Select(x => x.Result);
наawait Task.WhenAll(stuff)
Существующий код работает, но блокирует поток.
создает новую задачу для каждого события, но
блокирует поток, ожидающий завершения каждой новой задачи.
С другой стороны, ваш код дает тот же результат, но остается асинхронным.
Только один комментарий к вашему первому коду. Эта линия
создаст одну задачу, поэтому переменная должна быть названа в единственном числе.
Наконец ваш последний код делает то же самое, но более кратким
Для справки: Task.Wait / Task.WhenAll
источник
tasks
переменной, вы совершенно правы. Ужасный выбор, они даже не задачи, так как их ждут сразу. Я просто оставлю вопрос как естьС текущими методами, доступными в Linq, это выглядит довольно уродливо:
Надеемся, что следующие версии .NET предложат более элегантный инструментарий для обработки коллекций задач и задач коллекций.
источник
Я использовал этот код:
как это:
источник
Select()
, так что это элегантное раскрытие.async
Иawait
внутри первой лямбды является излишним. Метод SelectAsync можно просто записать так:return await Task.WhenAll(source.Select(method));
Я предпочитаю это как метод расширения:
Так что это можно использовать с цепочкой метода:
источник
Wait
когда он на самом деле не ждет. Это создание задачи, которая завершается, когда все задачи выполнены. Назовите этоWhenAll
, какTask
метод, который он эмулирует. Это также бессмысленно для методаasync
. Просто позвонитеWhenAll
и покончите с этим.WhenAll
возвращает оцененный список (он не оценивается лениво), можно указать аргумент, чтобы использоватьTask<T[]>
возвращаемый тип для обозначения этого. Когда ожидается, он все еще сможет использовать Linq, но также сообщит, что он не ленив.