实体框架异步方法是否使用 ThreadPool 线程?

Arm*_*our 9 c# asp.net entity-framework threadpool async-await

我通常EF Core在我的 Web 应用程序中使用许多异步方法,如下所示:

await db.Parents.FirstOrDefaultAsync(p => p.Id == id);
Run Code Online (Sandbox Code Playgroud)

我们知道,ThreadPool默认情况下,初始线程数仅限于 CPU 逻辑内核数。用户请求也由ThreadPool.

由于应用程序中有许多异步调用,我是否应该担心处理用户请求或性能问题?

Dav*_*oft 7

由于应用程序中有许多异步调用,我是否应该担心处理用户请求或性能问题?

EF Core 为存储库提供异步查询接口。无论是全异步还是特定方法阻塞线程池线程都依赖于 EF 提供程序。SQLServer 的 SqlClient 具有不阻塞线程的基于任务的异步方法。大多数其他提供商也这样做。但是例如对于 EF 内存中提供程序,或者 SQLite 提供程序,它可能是异步过同步,要么同步完成并返回已完成的任务,要么阻塞线程池线程。

所以EF通常不会阻塞你的线程。当您对数据库进行异步调用时,它会释放应用程序的线程来做更多的工作。喜欢处理额外的请求。如果您对数据库的并发请求过多,则每个请求将开始花费更多时间。

发生这种情况时,您需要有一种机制来减慢对数据库的新请求的速度,否则您将进入不良状态。数据库服务器所在的EG有2000个正在运行的请求,其中大部分代表已经放弃和超时的客户端。由于所有旧请求,新请求没有得到及时处理。

通常,当您将并发增加到某个点时,吞吐量会增加,但超过该点,整体吞吐量会下降,有时会急剧下降。像这样的东西:

在此处输入图片说明

由您决定限制整体并发以防止吞吐量严重下降。最好提前失败一些请求(例如使用 HTTP 503),而不是全部接受它们并且不在您的 SLA 内完成任何请求。

使用同步数据库访问的好处之一是它在数据库交互期间占用一个应用程序线程,自动向请求流添加背压。当所有线程池线程都忙时,请求必须等待线程池线程实际上是一件好事。当你完全异步时,这个控制就会消失,你需要考虑替换它。

ASP.NET Core 目前没有内置限制。您的 Web 服务器主机可能有一些,例如,SqlConnection 的连接池限制用于限制每个应用程序实例的并发请求数。但是您必须有一些东西可以让您以有序的方式处理请求量的激增。

  • @DavidBrowne-Microsoft,这取决于规模。请考虑 IIS 7.5 至 10、.NET Framework 4.5 至 4.8 上的 ASP.NET 以及 I/O 密集型 Web 应用或服务。将其中一些转换为异步 I/O 后,每个线程都会同时使用 1/500 到 1/1000 的线程。之前,当 TP max=2000 时,它被限制为 2000 个并发请求,现在它可以处理 20000+ 个并发请求,同时最大线程数约为 25 个。由于共享应用程序池内存减少而节省的云资源允许对数据库进行投资。甚至免费和共享的 Azure 托管也成为测试开发版本的选项。 (3认同)
  • 我认为 OP 担心 EF 可能会在后台阻塞线程,这与异步方法[预期的行为]相反(https://blog.stephencleary.com/2013/11/there-is-无线程.html)。你的答案是:*当所有线程池线程都忙时,请求必须等待线程池线程实际上是一件好事。*或者换句话说:“不要担心它是否会阻塞线程,因为有一个饥饿的线程`ThreadPool` 是一件好事,因为它有助于控制数据库并发性。” (2认同)
  • 我认为同步使用“ThreadPool”(即阻塞 I/O)并不比异步使用(即异步 I/O)更好。事实上,前一个 a̶u̶t̶o̶m̶a̶t̶i̶c̶a̶l̶l̶y̶ 隐式地对较低层施加某种限制,这一事实并不比明确施加合理的限制更好,并且可能会扩展以下层。如果大型同步网络场或小型异步网络场的处理能力、内存或带宽都超出了数据库的处理能力,那么这对数据库有何影响?然而,Web 应用程序层的基础设施存在很大差异。 (2认同)
  • [这篇博文](https://docs.microsoft.com/en-us/archive/blogs/rickandy/should-my-database-calls-be-asynchronous)也(最初)建议使用同步调用。然而,这个答案和那篇博客文章中隐含的假设是有一个数据库后端服务器。在云世界中,“异步”是更自然的默认设置。 (2认同)