我有这样的方法。
public async Task<MyResult> GetResult()
{
MyResult result = new MyResult();
foreach(var method in Methods)
{
string json = await Process(method);
result.Prop1 = PopulateProp1(json);
result.Prop2 = PopulateProp2(json);
}
return result;
}
后来我决定用 Parallel.ForEach
:
public async Task<MyResult> GetResult()
{
MyResult result = new MyResult();
Parallel.ForEach(Methods, async method =>
{
string json = await Process(method);
result.Prop1 = PopulateProp1(json);
result.Prop2 = PopulateProp2(json);
});
return result;
}
但现在我遇到了一个错误。
一个异步模块或处理程序完成了,而一个异步操作还在等待。
async
不太适用于 ForEach
. 特别是,你的 async
lambda正被转换为 async void
的方法。有一些 避免的原因 async void
(正如我在MSDN的一篇文章中所描述的那样);其中之一是你无法轻松地检测到当 async
lambda已经完成。ASP.NET将看到您的代码在没有完成的情况下返回。async void
方法并(适当地)抛出一个异常。
你可能想要做的是处理数据 同时只是不在 平行. 并行代码几乎不应该在ASP.NET上使用。下面是异步并发处理时的代码。
public async Task<MyResult> GetResult()
{
MyResult result = new MyResult();
var tasks = Methods.Select(method => ProcessAsync(method)).ToArray();
string[] json = await Task.WhenAll(tasks);
result.Prop1 = PopulateProp1(json[0]);
...
return result;
}
另外,如果使用 AsyncEnumerator NuGet包。 你可以这样做。
using System.Collections.Async;
public async Task<MyResult> GetResult()
{
MyResult result = new MyResult();
await Methods.ParallelForEachAsync(async method =>
{
string json = await Process(method);
result.Prop1 = PopulateProp1(json);
result.Prop2 = PopulateProp2(json);
}, maxDegreeOfParallelism: 10);
return result;
}
哪儿 ParallelForEachAsync
是一个扩展方法。
Ahh, okay. 我想我现在知道是怎么回事了。 async method =>
是一个 "async void",是 "fire and forget"(除了事件处理程序外,不建议用于其他任何事情)。 这意味着调用者无法知道它何时完成... ... 所以。GetResult
返回的同时,操作仍在运行。 虽然我的第一个答案的技术细节是不正确的,但这里的结果是一样的:GetResult在返回的同时,由 ForEach
还在运行。 你唯一能做的就是不 await
关于 Process
(这样,lambda就不再是......) async
)并等待 Process
来完成每次迭代。 但是,这将使用至少一个线程池线程来做这件事,因此会给线程池带来轻微的压力--可能会利用 ForEach
毫无意义。 我干脆不使用Parallel.ForEach...