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

时间:2015-06-01 06:21:05

标签: c# asp.net asp.net-mvc-4 asynchronous async-await

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

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

8 个答案:

答案 0 :(得分:207)

你可能会找到我的MSDN article on the subject helpful;我在那篇描述的文章中占用了大量空间,你应该在ASP.NET上使用async,而不仅仅是如何在ASP上使用async .NET。

  

我有一些担心在ASP.NET MVC中使用异步操作。当它改善我的应用程序的性能时,以及何时 - 不是。

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

特别是不会:

  • 让您的个人请求更快完成。事实上,他们将完成(只是一点点)慢。
  • 点击await后返回来电者/浏览器。 await仅“产生”到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服务器集群,Azure SQL,NoSQL等 - 并且您的后端可以扩展,并且您的可伸缩性瓶颈是IIS,那么您可以从{获得可伸缩性优势{ {1}}。

  

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

尽可能多的人。但是,请注意,许多ORM具有每操作一个操作规则。特别是,EF只允许每个DbContext进行一次操作;无论操作是同步还是异步,都是如此。

另外,请记住后端的可扩展性。如果您正在使用单个SQL Server实例,并且您的IIS已经能够使SQLServer保持满负荷运行,那么SQLServer上的压力加倍或三倍就不会对您有所帮助。

答案 1 :(得分:94)

当操作必须执行多个独立的长时间运行操作时,

异步操作方法非常有用。

  

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

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

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

应该查看12个参考

来自@PanagiotisKanavos评论:

  

此外,async并不意味着并行。异步执行释放了一个   来自阻止外部资源的有价值的线程池线程   没有复杂性或性能成本。这意味着相同的IIS机器可以   处理更多的并发请求,而不是它会更快地运行。

     

您还应该考虑阻止呼叫以a开头   CPU密集型spinwait。在压力时期,阻塞呼叫会   导致延迟和应用程序池回收不断升级。异步调用   简单地避免这个

答案 2 :(得分:19)

  

在ASP.NET MVC中到处使用异步操作是否合适?

与编程一样,取决于。沿着某条道路走下去总会有一个权衡。

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

需要注意的是,这台状态机有一个微妙的开销。使方法异步不会使其执行得更快,这是很多人理解和误解的重要因素。

使用async-await时需要考虑的另一件事是它异步,这意味着你会看到异步穿透你的整个调用堆栈,顶部到底部。这意味着如果您想要公开同步API,您通常会发现自己会复制一定数量的代码,因为asyncsync不能很好地混合。

  

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

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

  

我可以使用等待关键字查询数据库的次数   在单一动作方法中异步?

只要您遵循数据库提供商声明的规则,就可以随心所欲。您可以进行异步调用的数量没有限制。如果您的查询彼此独立且可以同时进行,则可以为每个查询创建一个新任务,并使用await Task.WhenAll等待两者完成。

答案 3 :(得分:7)

我的5美分:

  1. 当且仅当您执行IO操作时使用def self.find_events ,例如DB或外部服务Web服务。
  2. 始终首选对数据库的异步调用。
  3. 每次查询数据库时。
  4. P.S。第1点有例外情况,但您需要很好地理解异步内部。

    作为额外的优势,如果需要,您可以并行执行少量IO调用:

    async/await

答案 4 :(得分:6)

当操作对DB或某些网络绑定调用执行某些操作时,

async操作最有帮助,其中处理请求的线程在从DB或网络绑定调用中获得应答之前将被停止刚刚调用。你最好使用等待它们,它将真正提高你的应用程序的响应能力(因为在等待数据库或任何其他类似的操作时,较少的ASP输入\输出线程将被停止)。在我的所有应用程序中,每当多次调用DB非常必要时,我总是将它们包装在可用的方法中并使用await关键字进行调用。

答案 5 :(得分:3)

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

请查看here以获得出色的建议。

答案 6 :(得分:1)

  

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

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

  

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

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

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

我可以使用await关键字查询数据库多少次   单一动作方法中异步执行吗?

天空是极限

答案 7 :(得分:1)

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

我的建议是,仅在您知道会有所帮助的情况下使用

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

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