我使用IO的所有操作都应该是异步的吗

本文关键字:异步 IO 操作 | 更新日期: 2023-09-27 17:59:20

当我阅读MSDN文章在ASP.NET MVC 4中使用异步方法时,我得出的结论是,我应该始终使用异步等待进行I/O绑定操作。

考虑下面的代码,movieManager公开了类似ORM的实体框架的异步方法。

public class MovieController : Controller
{
    // fields and constructors
    public async Task<ActionResult> Index()
    {
        var movies = await movieManager.listAsync();
        return View(movies);
    }
    public async Task<ActionResult> Details(int id)
    {
        var movie = await movieManager.FindAsync(id);
        return View(movie);
    }
}
  1. 这是否总能为我提供更好的可扩展性和/或性能?
    • 我该如何测量
  2. 为什么这在"现实世界"中没有使用
  3. 上下文同步怎么样?
    • 我不应该在ASP.NET MVC中使用异步I/O,这有那么糟糕吗

我知道有很多问题,但关于这个主题的文献有着相互矛盾的结论。有人说,在依赖I/O的任务中应该始终使用异步,也有人说在ASP.NET应用程序中根本不应该使用异步。

我使用IO的所有操作都应该是异步的吗

这是否总能为我提供更好的可扩展性和/或性能?

可能吧。如果你只有一个数据库服务器作为你的后端,那么你的数据库可能是你的可扩展性瓶颈,在这种情况下,扩展你的网络服务器对你的整个服务范围没有任何影响。

我该如何测量?

带负载测试。如果你想要一个简单的概念证明,你可以看看我的要点。

为什么这在"现实世界"中没有经常使用?

的确如此。.NET 4.5之前的异步请求处理程序编写起来相当痛苦,许多公司只是在这个问题上投入了更多的硬件。现在,.NET 4.5和async/await获得了很大的发展势头,异步请求处理将继续变得更加普遍。

上下文同步怎么样?

它由ASP.NET为您处理。我在博客上有一个async介绍,解释了当您await执行任务时,await将如何捕获当前的SynchronizationContext。在这种情况下,它是一个代表请求的AspNetSynchronizationContext,所以像HttpContext.Current、区域性等都会自动保留在await点上。

我不应该在ASP.NET MVC中使用异步I/O,这有那么糟糕吗?

一般来说,如果使用.NET 4.5,则应该使用async来处理任何需要I/O的请求。如果请求很简单(即没有命中数据库或调用另一个服务),那么只需保持同步即可。

这是否总能为我提供更好的可扩展性和/或性能?

你自己回答了,你需要测量并找出答案。通常,由于增加了复杂性,异步是稍后要添加的内容,这是代码库中的首要问题,直到您遇到特定问题为止。

我该如何测量?

双向构建,看看哪个更快(最好用于大量操作)

为什么这在"现实世界"中没有经常使用?

因为复杂性是软件开发中最大的问题。如果代码很复杂,那么它更容易出错,也更难调试。更重要的是,更难修复的错误并不是潜在性能优势的好交换。

上下文同步怎么样?

我假设你指的是ASP.NET上下文,如果是这样的话,你就不应该有任何同步,确保只有一个线程访问你的上下文并通过它进行通信

我不应该在ASP.NET MVC中使用异步I/O,这有那么糟糕吗?

引入异步只是为了处理同步是一种损失,除非你真的需要性能。

在网站中放置异步代码有很多负面影响:

  • 当数据片段之间存在依赖关系时,您会遇到麻烦,因为您无法实现异步
  • 异步工作通常是为API请求这样的事情而完成的。你有没有想过你不应该在网页上做这些?如果外部服务中断,你的网站也会中断。这不成规模
  • 在某些情况下,异步操作可能会加快网站的速度,但基本上会带来麻烦。你总是在等待最慢的一个,因为有时资源会因为任何原因而减慢速度,这意味着网站速度减慢的风险会增加一个等于你正在使用的异步作业数量的因素。您必须引入超时来处理这些问题,然后引入错误处理代码等
  • 当由于CPU负载太重而扩展到多个Web服务器时,异步工作会对您造成伤害。现在,在用户单击链接的那一刻,您过去放在异步代码中的所有内容都会同时启动,然后放松。这不仅适用于CPU负载,还适用于数据库负载甚至API请求。您将在所有系统资源中看到一种非常糟糕的利用率模式:大量使用的峰值,然后再次下降。这规模不大。同步代码没有这个问题:作业只有在另一个作业完成后才开始

网站的异步工作是一个陷阱:不要去那里

将繁重的代码放在一个worker(或cron作业)中,在用户要求之前完成这些工作。您可以将它们保存在数据库中,并且可以继续向站点添加功能,而不必担心会触发太多异步作业等等。

网站的性能被严重高估了。当然,如果你的页面在50ms内渲染是很好的,但如果需要250ms,人们真的不会注意到(测试一下:在代码中放入Sleep(200))。

如果你只是将工作转移到另一个进程,并使网站成为只访问数据库的接口,你的代码就会变得更具可扩展性。不要让你的网络服务器做不该做的繁重工作,它不会扩展。你可以让一百台机器在每个网页上总共花费1个CPU小时,但至少它可以在200毫秒内加载页面。祝你用异步代码实现这一点。


我想在这里添加一个旁注。虽然我对异步代码的看法可能很强烈,但它主要是对程序员的看法。异步代码非常棒,可以带来性能差异,这证明了我所概述的所有要点都是错误的。然而,它需要在代码中进行大量的微调,以避免我在这篇文章中提到的要点,而大多数程序员就是无法处理这一点