【问题标题】:Unexpected different results using Task.WhenAll and Parallel.ForEach with an Entity Framework Core DataContext将 Task.WhenAll 和 Parallel.ForEach 与 Entity Framework Core DataContext 一起使用时出现意外的不同结果
【发布时间】:2021-06-10 06:15:43
【问题描述】:

鉴于此 Entity Framework Core sn-p

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

以及我想以并发方式使用它的实体列表。我第一次尝试

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

这在 SaveChangesAsync 中因并发问题 (DataContext) 失败。 然后我尝试了

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

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

【问题讨论】:

  • @IvanStoev Task.WhenAll 实际上确实适用于 IEnumerable,无需具体化它 (docs.microsoft.com/en-us/dotnet/api/…)
  • 目标数据库是什么?有没有可能是Oracle
  • 数据库是 SQL Server
  • 附带说明,Parallel.ForEach 方法 is not async-friendly。使用异步委托提供 Parallel.ForEach 是一个错误。它没有做你期望它做的事情。
  • await Task.WhenAll(...成功的一个可能原因是异步操作实际上是同步执行的,所以它们是序列化的。您能否使用日志记录或其他方式验证SaveChangesAsync 操作确实是同时执行的?

标签: c# concurrency entity-framework-core


【解决方案1】:

我想了解 Foreach 版本失败时。

ForEach 不理解 async lambdas。由于ForEach 参数的类型为Action,因此async lambda 转换为async void 方法。 problems with async void methods 之一是调用代码很难确定它何时完成,因此 ForEach 循环似乎“提前”结束 - 在 async void lambda 完成之前。

async void 被添加到语言中以启用 async 事件处理程序,但这不是事件处理程序,因此在这里使用 async void lambdas 是不正确的。

【讨论】:

    【解决方案2】:

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

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

    编辑

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

    【讨论】:

    • 我不知道微软是否愿意在Parallel.ForEach 的文档中添加警告(如果你愿意,你可以通过try 来实现),但好消息是一个新的 API Parallel.ForEachAsync 可能会包含在即将到来的 .NET 6 中,专为接受 acync 委托而设计。希望这一新增功能将使Parallel.ForEach 不再像现在这样陷入困境。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-06-16
    • 1970-01-01
    • 2013-10-06
    • 1970-01-01
    • 2016-10-31
    • 1970-01-01
    • 2021-03-21
    相关资源
    最近更新 更多