Когда вы используете LINQ, обычно есть две части: создание и итерация.
Создание:
var query = list.Select( a => a.Name);
Эти вызовы всегда синхронны.Но этот код не делает намного больше, чем создает объект, который предоставляет IEnumerable.Фактическая работа не будет выполнена позже, из-за паттерна, называемого отложенное выполнение .
Итерация:
var results = query.ToList();
Этот код принимает перечислимое значение и получает значение каждого элемента, что обычно включает вызов ваших делегатов обратного вызова (в данном случае a => a.Name
).Это та часть, которая потенциально дорогая и может извлечь выгоду из асинхронности, например, если ваш обратный вызов - что-то вроде async a => await httpClient.GetByteArrayAsync(a)
.
Так что это итерационная часть, в которой мы заинтересованы, если мы хотим сделать этоasync.
Проблема здесь в том, что ToList()
(и большинство других методов, которые вызывают итерацию, например Any()
или Last()
) не являются асинхронными методами, поэтому ваш делегат обратного вызова будет вызываться синхронно,и вы получите список задач вместо нужных вам данных.
Мы можем обойти это с помощью такого кода:
public static class ExtensionMethods
{
static public async Task<List<T>> ToListAsync<T>(this IEnumerable<Task<T>> This)
{
var tasks = This.ToList(); //Force LINQ to iterate and create all the tasks. Tasks always start when created.
var results = new List<T>(); //Create a list to hold the results (not the tasks)
foreach (var item in tasks)
{
results.Add(await item); //Await the result for each task and add to results list
}
return results;
}
}
С помощью этого метода расширения, мы можем переписать ваш код:
var results = await inputs.Select( async i => await InnerMethodAsync(i) ).ToListAsync();
^ Это должно дать вам асинхронное поведение, которое вы ищете, и избежать создания задач пула потоков, как в вашем примере.
Примечание:Если вы используете LINQ-to-entity, дорогая часть (получение данных) вам не доступна.Для LINQ-to-entity вы можете использовать ToListAsync () , который поставляется вместе с EF Framework.
Попробуйте и посмотрите временные характеристики в моей демонстрации на DotNetFiddle .