Что на самом деле происходит при использовании async/await внутри оператора LINQ?
следующий фрагмент компилируется, но я ожидаю, что он будет ждать результата задачи, а не давать мне List<Task<T>>
.
var foo = bars.Select(async bar => await Baz(bar)).ToList()
как указал здесь, вы должны использовать Task.WhenAll
:
var tasks = foos.Select(async foo => await DoSomethingAsync(foo)).ToList();
await Task.WhenAll(tasks);
но комментарий указывает, что async
и await
внутри Select()
не нужен:
var tasks = foos.Select(foo => DoSomethingAsync(foo)).ToList();
аналогичный вопрос здесь где кто-то пытается использовать асинхронный метод Where()
.
так async
и await
внутри оператора LINQ есть юридический синтаксис, но он вообще ничего не делает или имеет определенное использование?
2 ответов
я рекомендую вам не думать об этом как "с помощью async
в LINQ в". Имейте в виду, что между ними: делегаты. Несколько операторов LINQ принимают делегатов и async
можно использовать для создания асинхронного делегата.
Итак, когда у вас есть асинхронный метод BazAsync
что возвращает Task
:
Task BazAsync(TBar bar);
этот код приводит к последовательности задач:
IEnumerable<Task> tasks = bars.Select(bar => BazAsync(bar));
аналогично, если вы используете async
и await
в делегате, вы создаете асинхронный делегат, который возвращает Task
:
IEnumerable<Task> tasks = bars.Select(async bar => await BazAsync(bar));
эти два выражения LINQ функционально эквивалентны. Нет никаких существенных различий.
как и обычные выражения LINQ,IEnumerable<Task>
лентяй-оценка. Только с асинхронными методами, такими как BazAsync
, вы обычно делаете не хотите случайную двойную оценку или что-то в этом роде. Поэтому, когда вы проецируете последовательность задач, обычно это хорошая идея немедленно повторите последовательность. Это зовет BazAsync
для всех элементов в исходной последовательности, начиная всеми задачами будут:
Task[] tasks = bars.Select(bar => BazAsync(bar)).ToArray();
конечно, все, что мы сделали с Select
is старт асинхронная операция для каждого элемента. Если вы хотите дождаться их завершения, используйте Task.WhenAll
:
await Task.WhenAll(tasks);
большинство других операторов LINQ не работают так чисто с асинхронными делегатами. Select
довольно просто: вы только начинаете асинхронная операция для каждого элемента.
имеет ли он определенное использование
конечно. С async и await внутри оператора LINQ вы можете, например, сделать что-то вроде этого:
var tasks = foos.Select( async foo =>
{
var intermediate = await DoSomethingAsync( foo );
return await DoSomethingElseAsync( intermediate );
} ).ToList();
await Task.WhenAll(tasks);
без async / await внутри оператора LINQ вы ничего не ждете внутри оператора LINQ, поэтому вы не можете обработать результат или ожидать чего-то другого.
без async / await в инструкции LINQ вы только начинаете задачи, но не ждете их завершения. Они все равно завершите в конце концов, но это произойдет еще долго после того, как элемент управления покинет оператор LINQ, поэтому вы можете получить доступ к их результатам только после WhenAll
строка будет завершена, но не внутри оператора LINQ.