【问题标题】:Best way to wait for SqlClient calls in ASP.Net Core 3.1在 ASP.Net Core 3.1 中等待 SqlClient 调用的最佳方法
【发布时间】:2021-03-18 02:39:16
【问题描述】:

我创建了一个带有 Angular 前端和 ASP.Net Core 3.1 后端的 SPA 应用程序。我使用典型的 RESTful API 在两者之间进行通信,以及基于 SignalR 的 WebSocket API 用于一些实时推送状态更新。因为这个应用程序是对旧的 Windows .NET 程序的重写,所以我有一个大量的代码库,它使用 SqlClient 与公共数据库进行交互。我想重用此代码,而不是尝试将系统转换为 EF。我已经完成了所有这些工作,但我开始担心对 ExecuteScalar、ExecuteReader 和 ExecuteNonQuery 等函数的调用可能会对在 IIS 中运行的 ASP.Net 后端的性能产生负面影响。我还需要考虑 GetDataAdapter 和 adapter.Fill 但这是一个不同的问题。

似乎我应该使用异步调用来帮助线程管理,但如果我所做的只是立即等待调用,那么使用同步调用有什么不同吗?同步调用是否在 IIS 中很好地等待?

需要明确的是,我没有发现使用同步或异步调用有任何问题。我的早期测试没有负载。我希望我的应用程序可以扩展到大约 100 个并发用户,可能使用云主机。这就是为什么我要做出正确的基本架构决策。

如果重要的话,我的后端在 .NET 5 中无需更改即可运行,但我尚未承诺升级。如果 .NET 5 可以提高我的性能,我会考虑升级。

【问题讨论】:

  • 您可以通过使数据库调用异步来提高网络服务器的容量,只要触发它们的控制器操作也是异步的 - 因为它在数据库调用运行时释放了 IIS 工作线程.因此,如果您的服务器已达到或接近容量,那么您可能会立即注意到一些改进,或者至少它释放了容量以供将来扩展。
  • 如果你的入口代码是async,最好使用ADO.NET异步调用,避免同步和异步代码混用。几乎所有 ADO.NET 函数都有异步等效函数。文档中的更多详细信息:docs.microsoft.com/en-us/dotnet/framework/data/adonet/…。性能改进可能会或可能不会立即注意到 - 这取决于您的负载;主要好处是避免在等待数据库调用完成时阻塞 ThreadPool 线程。见blog.stephencleary.com/2013/11/there-is-no-thread.html
  • 谢谢大家。我花费大量时间使所有数据库访问异步(包括将大量 GetDataAdapter-adapter.Fill 调用转换为 ExecuteReader.ReadAsync 调用),然后我开始担心我在浪费时间。当然,同步调用在等待时不会旋转,是吗?

标签: c# asp.net sql-server async-await sqlclient


【解决方案1】:

但如果我所做的只是立即等待调用,那么使用同步调用有什么不同吗?

是的,有一个非常显着的区别:它释放线程,以便该线程不会被网络 IO(以及您的数据库服务器正在做什么)阻塞,而是可以去做其他有趣的事情。 Await 完全是关于 CPU 资源(尤其是线程)的可扩展性,但涉及到一些小开销,这可能会使 单个 请求稍微变慢,但不会达到您所测量的任何数量。

但是对于上下文:堆栈溢出(负载比这高得多)在 IIS 上可以很好地扩展(它也在 Kestrel 中运行 所需的)使用大部分同步数据库访问(转换为异步数据访问仍在进行中)。如果您的应用程序不能扩展到 100 个用户,那么将责任归咎于同步与异步将是在推卸责任:真正的问题可能是由于次优设计导致查询性能不佳。异步不会造成或破坏它 - 它只会帮助设计良好的系统进一步扩展。

【讨论】:

  • 感谢您的意见。所以同步调用在等待时不会休眠?还是他们有其他一些损害多线程性能的行为?这将是我能想到的 Async 调用可能表现更好的唯一情况。
  • 我的理解是,每个 REST API 调用都在专用线程上执行,并且控制器上下文专用于该调用,直到它被解决。这就是为什么我认为同步调用是可以接受的,只要它们不做像自旋锁这样的坏事。
  • @user2242061 " 所以同步调用在等待时不会休眠?" ...这是一种表达方式。 await 允许调用代码有效地“暂停”,并释放它运行的线程供其他代码使用。同时,正在等待的代码正在执行非 CPU 绑定操作,例如网络或数据库调用,它没有使用 IIS 可以使用的线程。而在完全同步的设置中,一切都在单个线程上运行,即使代码当前正在执行非 CPU 绑定操作,该线程也不会被释放。
  • @user2242061 换一种说法:线程比较昂贵,所以系统尽量不要太多;如果他们阻止等待 IO,则您无法处理其他请求。是冷等待(sleep),不是热等待(loop),但效果是一样的:线程没用。 Await (async) 通过展开线程来解决这个问题,让它做有用的工作,而不需要额外的线程。对于相同的线程数,您可以获得更多的工作。
  • 感谢您和@ADyson,我终于明白了这一点。释放线程意味着暂停当前的执行上下文并让线程切换到另一个不再被阻塞的上下文。乘以有限线程池的效果来处理多倍的并发任务。这确实帮助我理解了 SqlClient 和 Async-Await 在 ASP.Net 中的工作原理。谢谢。
猜你喜欢
  • 2021-02-06
  • 2021-01-18
  • 2021-09-19
  • 1970-01-01
  • 2015-02-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多