什么时候应该在ASP.NET MVC中使用异步控制器?


215

我在ASP.NET MVC中使用异步操作时有些担心。什么时候可以改善我的应用程序的性能,什么时候不可以

  1. 在ASP.NET MVC的任何地方使用异步操作是否很好?
  2. 关于等待方法:当我要查询数据库时(通过EF / NHibernate /其他ORM),我应该使用async / await关键字吗?
  3. 一个单一的操作方法可以使用await关键字异步查询数据库多少次?

Answers:


109

当一个动作必须执行几个独立的长时间运行的操作时,异步动作方法很有用。

AsyncController类的典型用法是长时间运行的Web服务调用。

我的数据库调用应该异步吗?

IIS线程池通常可以比数据库服务器处理更多的同时阻止请求。如果数据库是瓶颈,异步调用将不会加快数据库响应速度。如果没有限制机制,则通过使用异步调用有效地将更多工作分配给不堪重负的数据库服务器,只会将更多的负担转移到数据库上。如果您的数据库是瓶颈,那么异步调用将不是魔术。

应该看一下12参考

来自@PanagiotisKanavos评论:

而且,异步并不意味着并行。异步执行使宝贵的线程池线程免于阻塞外部资源,而没有任何复杂性或性能成本。这意味着同一台IIS机器可以处理更多的并发请求,而不是运行得更快。

您还应该考虑到阻塞调用始于占用大量CPU的Spinwait。在压力时期,阻止调用将导致延迟升级和应用程序池回收。异步调用只是避免这种情况


27
关于异步,IIS和数据库,该博客文章非常古老,此处得出的结论是错误的。IIS具有服务器比数据库较大的负载,线程池线程是有限的,一个长请求队列可以导致500凡是在等待IO腾出一个线程是有益的。甚至链接也与OP的要求无关。实际上,同
一位

30
而且,异步并不意味着并行。异步执行使宝贵的线程池线程免于阻塞外部资源,而没有任何复杂性或性能成本。这意味着同一台IIS机器可以处理更多的并发请求,而不是运行得更快。
Panagiotis Kanavos 2015年

5
您还应该考虑到阻塞调用始于占用大量CPU的Spinwait。在压力时期,阻止调用将导致延迟升级和应用程序池回收。异步调用只是避免了这种情况
Panagiotis Kanavos 2015年

1
由于这是公认的答案,因此对于大多数人来说都是最重要的答案,因此删除有关执行时间并并行运行的第二个第一段可能是明智的选择,这与异步无关。我知道在最后有一个纸条,但它非常容易引起误解。
罗布

1
异步执行仅在真正异步到实现的最底层时才释放线程,并使用某种回调机制来表示准备就绪并启动新线程来切换上下文并处理结果。因此,我不确定在哪种情况下此上下文切换/新线程创建比仅在单个线程中等待结果更有效?无论如何,长期运行Web服务调用是一个坏主意,我更喜欢将其卸载到可靠的后台服务中,然后让客户端检查结果,这可能需要几分钟,几小时或几天的时间。
JustAMartin19年

229

您可能会发现有关该主题的我的MSDN文章很有帮助;在那篇文章中,我花了很多篇幅来描述何时应该async在ASP.NET上使用,而不仅仅是如何async在ASP.NET 上使用。

我在ASP.NET MVC中使用异步操作时有些担心。什么时候可以提高我的应用程序的性能,什么时候可以-不能。

首先,了解async/ await全部与释放线程有关。在GUI应用程序上,主要是要释放GUI线程,以便更好的用户体验。在服务器应用程序(包括ASP.NET MVC)上,主要是关于释放请求线程以便服务器进行扩展。

特别是,它不会:

  • 使您的个人请求更快地完成。实际上,它们的完成速度会变慢(只是有点小)。
  • 按下时,返回到呼叫者/浏览器awaitawait仅“屈服”到ASP.NET线程池,而不是浏览器。

第一个问题是-在ASP.NET MVC的任何地方使用异步操作是否很好?

我想说,在进行I / O的任何地方都可以使用它。不过,它不一定是有益的(请参阅下文)。

但是,将其用于受CPU约束的方法很不好。有时,开发人员认为async只要调用Task.Run其控制器就可以从中受益,这是一个可怕的想法。由于该代码最终通过占用另一个线程来释放请求线程,因此根本没有任何好处(实际上,它们承担了额外的线程切换的代价)!

当我要查询数据库时(通过EF / NHibernate /其他ORM),我应该使用async / await关键字吗?

您可以使用任何可用的等待方法。目前,大多数主要参与者都支持async,但有些则不支持。如果您的ORM不支持async,则不要尝试将其包装Task.Run或类似的包装(请参见上文)。

请注意,我说“您可以使用”。如果您在谈论具有单个数据库后端的ASP.NET MVC,那么(几乎可以肯定)您不会从中获得任何可伸缩性的好处async。这是因为IIS比SQL Server(或其他经典RDBMS)的单个实例处理的并发请求要多得多。但是,如果后端更现代-SQL Server群集,Azure SQL,NoSQL等-并且后端可以扩展,并且可伸缩性瓶颈是IIS,那么您可以从中获得可伸缩性async

第三个问题-我可以使用一次单一操作方法中的await关键字异步查询数据库多少次?

随你喜欢。但是,请注意,许多ORM都有“每个连接一个操作”的规则。尤其是,EF仅允许每个DbContext执行单个操作。无论操作是同步还是异步,都是如此。

此外,请再次记住后端的可伸缩性。如果您遇到的是SQL Server的单个实例,并且您的IIS已经能够使SQLServer保持满负荷运行,那么对SQLServer施加的压力加倍或增加三倍将完全无济于事。


2
@seebiscuit:在执行(适当的异步)I / O时,不使用任何线程。我有一篇博客文章,其中有更详细的介绍。
Stephen Cleary

2
IIS和一个SQL Server实例可以处理多少个请求?我如何找到这些号码?
MOD

1
@MOD:这取决于配置和硬件。查找这些数字的唯一方法是在您自己的服务器上进行负载测试。
史蒂芬·克利西

1
@Flezcano:在CPU上完全执行的方法。即,它们不进行I / O或阻塞。
史蒂芬·克雷里

1
对不起,先生,但是我不会接受我试图理解的这2个线性问题。当我在研究这个东西时,例如,如果我有1个Web api端点,同时被1000个用户调用,那么该端点将能够服务器这千个请求中的每一个,还是我应该使其异步处理1000个请求?
Learning-Overthinker-Confused

21

在ASP.NET MVC的任何地方都使用异步操作是否很好?

和编程一样,它取决于。沿着某个路径前进时总会有一个权衡。

async-await在您知道您将收到对服务的并发请求并且希望能够很好地扩展的地方很有帮助。如何async-await帮助横向扩展?事实上,当您同步调用异步IO调用(例如网络调用或命中数据库)时,负责执行的当前线程将被阻塞,等待请求完成。当使用时async-await,将使框架能够为您创建一个状态机,以确保在IO调用完成后,方法从中断处继续执行。

需要注意的是,该状态机的开销很小。使方法异步并不能使其执行速度更快,这是要理解的重要因素,也是许多人的误解。

使用时要考虑的另一件事async-await是它一直都是异步的,这意味着您将看到异步贯穿整个调用堆栈,从上到下。这意味着,如果您想公开同步API,您经常会发现自己在复制一定数量的代码,因为异步同步的混合效果不是很好。

当我要查询数据库时(通过EF / NHibernate /其他ORM),我应该使用async / await关键字吗?

如果您选择沿着使用异步IO调用的路径前进,那么是的,这async-await将是一个不错的选择,因为越来越多的现代数据库提供程序公开了实现TAP(任务异步模式)的异步方法。

一个单一操作方法中可以使用await关键字异步查询数据库多少次?

只要您遵循数据库提供者规定的规则,就可以任意数量。您可以进行的异步调用数量没有限制。如果您有彼此独立且可以同时进行的查询,则可以为每个查询旋转一个新任务,并使用它们await Task.WhenAll来等待两个查询完成。


2
异步db调用的执行速度并不快,但由于不会浪费线程池线程,因此允许同一台IIS计算机处理更多请求。这也降低了CPU成本,因为在实际阻塞之前,阻塞调用实际上是从占用大量CPU的Spinwait开始的。在高负载情况下,这可能是机器挣扎或故障与回收之间的差异
Panagiotis Kanavos 2015年

我知道@PanagiotisKanavos,我所说的不清楚吗?
Yuval Itzchakov

1
您的答案没有提到IIS的详细信息-崩溃/回收的情况是始终使用异步的主要原因。没有经历过的人可能不会理解那的scale out well意思your server won't die under load。或者可能是once burned ...
Panagiotis Kanavos

7

我的5美分:

  1. 使用async/await当且仅当你做一个IO操作,如数据库或外部服务web服务。
  2. 总是喜欢异步调用数据库。
  3. 每次查询数据库。

PS:第1点有特殊情况,但您需要对此有一个很好的了解。

另外一个优点是,如果需要,您可以并行执行几个IO调用:

Task task1 = FooAsync(); // launch it, but don't wait for result
Task task2 = BarAsync(); // launch bar; now both foo and bar are running
await Task.WhenAll(task1, task2); // this is better in regard to exception handling
// use task1.Result, task2.Result

6

async当操作对数据库或某些网络绑定的调用执行一些I \ O操作时,这些操作将提供最佳帮助,在这些操作中,处理请求的线程将在从您刚刚调用的DB或网络绑定的调用中获取答案之前被暂停。最好将它们与await一起使用,它确实会提高应用程序的响应能力(因为在等待数据库或其他任何类似操作时,ASP输入\输出线程会停滞不前)。在我所有的应用程序中,每当非常需要对数据库进行多次调用时,我总是将它们包装在awaiatable方法中,并使用await关键字进行调用。


5

如您所知,MVC支持异步控制器,您应该利用它。如果您的控制器执行冗长的操作(它可能是基于磁盘的I / o或对另一个远程服务的网络调用),如果以同步方式处理请求,则IIS线程将一直处于繁忙状态。结果,线程只是在等待冗长的操作完成。在进行第一个请求的操作时,可以通过处理其他请求来更好地利用它。这将有助于处理更多并发请求。您的Web服务将具有高度可扩展性,并且不会轻易遇到C10k问题。对数据库查询使用异步/等待是一个好主意。是的,您可以根据需要多次使用它们。

看看这里的优秀建议。


3

我的经验是,如今许多开发人员都将async/await其用作控制器的默认设置。

我的建议是,只有在您知道它会帮助您时才使用它

原因是,正如Stephen Cleary和其他人已经提到的那样,它可能会引入性能问题,而不是解决它们,并且仅在特定情况下会为您提供帮助:

  • 高流量控制器
  • 可扩展后端

2

在ASP.NET MVC的任何地方使用异步操作是否很好?

最好在任何可以使用异步方法的地方执行此操作,尤其是当您在工作进程级别出现性能问题时(发生在海量数据和计算操作中)。否则,不需要,因为单元测试将需要转换。

关于等待方法:当我要查询数据库时(通过EF / NHibernate /其他ORM),我应该使用async / await关键字吗?

是的,最好对所有数据库操作都使用异步,以避免在工作进程级别出现性能问题。请注意,EF为大多数操作创建了许多异步替代方法,例如:

.ToListAsync()
.FirstOrDefaultAsync()
.SaveChangesAsync()
.FindAsync()

一个单一的操作方法可以使用await关键字异步查询数据库多少次?

天空才是极限


大多数Web应用程序的业务逻辑通常需要高度顺序的操作,因此在大多数情况下,在并行进程之间进行跳转仅在Web请求/工作进程管理的最高级别才可行,而Web请求/工作进程管理始终无法直接处理数据库请求。我真的希望看到一些典型的Web应用程序的真实示例,其中以异步方式处理数据库查询实际上是一个好主意。
JustAMartin19年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.