使用具有实体框架核心数据上下文的Task.WhenAll和Parallel.ForEach时出现意外的不同结果



给定此实体框架核心片段

using (var scope = serviceProvider.CreateScope()) {
var ctx = scope.ServiceProvider.GetRequiredService<IEFCoreDataContext>();
//  async operations on ctx and entity (see below for entity)
await ctx.SaveChangesAsync();
}

并且列表<gt;我想以并发的方式使用它。我第一次尝试

await Task.Run(() => Parallel.ForEach(list, async entity => {
<snippet>
});

由于并发问题(DataContext(,此操作在SaveChangesAsync中失败。然后我尝试了

await Task.WhenAll(
list.Select(
async entity => {
<snippet>
}
)
);

这是成功的。我知道他们的做法不同(ForEach对输入列表进行分区,…(,但我想了解ForEach版本失败时的情况。

我想了解Foreach版本失败的原因。

ForEach不理解asynclambdas。由于ForEach参数的类型为Action,因此asynclambda被转换为async void方法。async void方法的一个问题是调用代码不容易确定它何时完成,因此ForEach循环似乎结束了;早"-在CCD_ 9 Lambda完成之前。

async void添加到该语言中是为了启用async事件处理程序,但这不是一个事件处理程序。因此在此处使用async voidlambdas是不正确的。

事实证明,这似乎需要在.NET方面进行改进,例如:

  • 编译器:检测并禁止在异步事件处理程序之外使用异步void(在本例中是直接使用或作为转换的结果(
  • 文档:在官方文档中明确说明Parallel.ForEach不应使用异步处理程序调用
  • 文档:如果使用async是可能的,但需要特别注意,请展示工作/正确使用的示例

编辑

多亏了Theodor Zoulias的评论,ForEach似乎只适用于同步处理程序,而ForEachAsync正在开发中。不管微软愿意,如果当异步处理程序作为ForEach输入传递时,C#编译器弹出一个错误,那将是一件好事

最新更新