ASPNet Entity Framework 6 - EF6,在同一工作单元中混合异步和同步

ASPNet Entity Framework 6 - EF6, mixing async and sync in the same unit of work

我今天遇到了一些代码行,归结起来就是这样的:

using (var db = new AppDbContext())
{
    var foo = await db.Foos
        .Where(m => m.Name == "BuzzBaz" )
        .FirstAsync();

    foo.Name = "Quxx";
    db.SaveChanges();
}

请注意 select 查询是 async 但对 SaveChanges() 的调用不是。它是同步的!

这可能会造成任何问题吗?我是否应该重构它以使用 db.SaveChangesAsync(); 并且一切都很好?或者也许只是调用 First() 而不是 FirstAsync()?或者它可能是无害的并且可能对整体系统性能有一些好处?

我发现一些文档让我觉得完全删除 async 可能是一个不错的选择。 https://docs.microsoft.com/en-us/ef/ef6/fundamentals/async

In most applications using async will have no noticeable benefits and even could be detrimental. Use tests, profiling and common sense to measure the impact of async in your particular scenario before committing to it.

您的代码是“正确的”,这意味着它没有根本性的缺陷。

让我们分析一下发生了什么,以及我对它们的 2 美分:

  • 当你查询数据库(FirstAsync)时,使用await意味着指令将按顺序执行,但线程不会被阻塞(异步调用)并且可以用于其他事情。这可能是一件好事,如果这是一个服务器应用程序,您可能希望您的线程能够在等待数据库响应时处理其他请求。

  • 使用非异步 SaveChanges 然而会阻塞线程。这本身不是错误,但由于它也是对 db 的 I/O 操作,它可能会在很长一段时间内阻塞线程。如果您在这里也 await 使用异步版本,它确实看起来更“一致”。

这是个问题吗?这取决于。您的应用程序使用频繁吗?您的用户是否在重负载时遇到一些低反应性?那么在这种情况下,在保存时使用异步可能是一个潜在的改进。

否则,正如链接的 MSDN 文章中所建议的那样,在您知道它有影响之前,我的建议是不要太担心。

混合使用本身不是问题。

就个人而言,我也会选择异步 SaveChanges,以保持一致性并想象数据库写入是一些可能很慢的 I/O 操作。

如果是桌面应用程序,请确保您没有阻塞 UI 线程。

除了非常重要的编码一致性之外,还要考虑以下几点:

  1. 这是 DAL 库吗?您确定谁将使用它并且您确定代码可重用性永远不会成为问题吗?

如果答案是可能的,请坚持异步使用。前端 UI 将在保存期间被阻止,使其无响应。 Web API 使用 IO 线程来实现更好的线程管理:Simple description of worker and I/O threads in .NET

如果它是一个库,请确保对所有异步函数调用使用 ConfigureAwait(false)

  1. 即使使用 await,调用也是异步的,在另一个线程上的拓片 运行 方面,而原始线程正在等待。大多数时候,这作为性能可以忽略不计,但为什么不呢?

我通常将所有内容都设为异步,如果需要同步实现,我更喜欢使用同步版本制作代码副本。