use*_*000 5 .net c# linq asynchronous async-await
基本上我有一个类似的程序
var results = await Task.WhenAll(
from input in inputs
select Task.Run(async () => await InnerMethodAsync(input))
);
.
.
.
private static async Task<Output> InnerMethodAsync(Input input)
{
var x = await Foo(input);
var y = await Bar(x);
var z = await Baz(y);
return z;
}
Run Code Online (Sandbox Code Playgroud)
我想知道是否有一种奇特的方式将它组合成一个LINQ查询,就像一个"异步流"(我可以描述它的最佳方式).
使用LINQ时,通常有两个部分:创建和迭代.
创建:
var query = list.Select( a => a.Name);
Run Code Online (Sandbox Code Playgroud)
这些调用始终是同步的.但是这个代码除了创建一个公开IEnumerable的对象之外没有什么作用.由于称为延迟执行的模式,实际工作直到稍后才会完成.
迭代:
var results = query.ToList();
Run Code Online (Sandbox Code Playgroud)
此代码获取可枚举并获取每个项的值,这通常涉及调用您的回调委托(在本例中a => a.Name
).这是可能很昂贵的部分,并且可以从异步性中受益,例如,如果你的回调是类似的async a => await httpClient.GetByteArrayAsync(a)
.
所以这是我们感兴趣的迭代部分,如果我们想让它异步.
这里的问题是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;
}
}
Run Code Online (Sandbox Code Playgroud)
使用此扩展方法,我们可以重写您的代码:
var results = await inputs.Select( async i => await InnerMethodAsync(i) ).ToListAsync();
Run Code Online (Sandbox Code Playgroud)
^这应该为您提供您正在寻找的异步行为,并避免创建线程池任务,如您的示例所示.
注意:如果您使用LINQ到实体,则不会向您公开昂贵的部分(数据检索).对于LINQ到实体,您需要使用EF框架附带的ToListAsync().
试一试,看看我在DotNetFiddle的演示中的时间.