为什么EF 6教程使用异步调用?

19

最新的EF教程介绍了如何使用EF 6和MVC 5,似乎倾向于使用异步调用数据库,例如:

Department department = await db.Departments.FindAsync(id);

这是新的标准/最佳实践吗?

我不确定使用ASP.NET MVC进行此种开发风格有何好处。

可以有人评论一下这种模式吗?这是微软正在推广的新标准吗?


2
这让我想起了一个老笑话:“为什么EF 6教程使用异步调用?因为它可以。” - Sergey Kalinichenko
1
我觉得这很奇怪,而且它并没有真正提到使用异步/等待调用的任何内容。 - public static
我想看到基准测试来“证明”那一点(就算基准测试可以证明什么事情!)。我猜想在响应时间方面甚至可能更慢,但也许更具可扩展性。不过对于一个基本的CRUD应用程序,我认为这并不重要。 - public static
就我个人而言,我不喜欢这样的教程。我更希望教程尽可能简单,至少在一开始时如此。将Entity Framework与async/await混合使用,意味着新手必须同时学习两件事情。 - Bradley Uffner
3个回答

47
为了决定是采用异步还是同步方式,请比较其优缺点: 异步:
  • 使用异步方法几乎不会耗尽线程池(除非情况极端)
  • 可以实现任意级别的并发性(并发请求和操作)
  • 每个线程节约1MB内存
  • 通过SynchronizationContext实现安全的请求内并发处理
  • 由于降低操作系统调度开销,可在高负载的情况下提高吞吐量百分之十几。然而,几乎没有生产应用程序处于高CPU负载状态,因为如果是这样,它已接近无法使用(在负载急剧增加的情况下,应用程序开始丢弃请求)
同步:
  • 代码更简单:使用await关键字让99%的情况(几乎)与同步代码一样简单。但是,堆栈溢出网站每天有10多个异步问题需要解答。当你偏离简单路径时,你会遇到一些边缘问题。使用需要传递同步回调的传统库时也是如此。
  • 编码和调试工作量更少
  • 易于分析性能(你可以对应用程序进行分析,或者暂停调试器并查看应用程序正在做什么。这对异步来说是不可能的)
  • 与传统代码和库完美兼容
如果您需要调用高延迟服务,请选择在ASP.NET中使用异步。 Web服务很可能是高延迟的。OLTP数据库几乎总是低延迟的。 如果您的应用程序从非常高的并发性(100+)中受益,请选择异步。 大多数应用程序没有这么高的级别,或者它们的后端服务无法承受这样的负荷。让Web应用程序扩展但使后端过载是没有意义的。调用链中的所有系统都必须从高度并发中受益,异步才能发挥作用。 典型的高延迟服务(异步良好的情况):
  • Web服务
  • 等待(例如睡眠)
  • 节流(SemaphoreSlim, ...)
  • 一些云服务(Azure)
  • 长时间运行的数据库查询(例如报告或ETL)
  • 典型的低延迟服务(同步调用的好处):

    • 数据库调用:大多数OLTP查询都是低延迟的,因为您可以假设数据库服务器没有过载。没有必要向它投掷100个并发查询。这不会使它们更快完成。
    • 文件系统:与数据库相同。

    这些按照典型的情况进行分类。所有这些也可能属于相反的类别。

    你可以在同一个应用程序中混合使用同步和异步。 在其最佳状态下使用异步。

    那么为什么微软和Entity Framework团队推广异步使用呢?以下是我的主观看法:这可能是微软的内部政策。他们可能预计EF在客户端应用程序中的使用(对于此场景,异步非常适合)。或者,他们没有意识到异步数据库调用几乎总是浪费开发人员的时间而没有任何好处。大多数人没有意识到这一点,因为异步是现在的趋势。


    @valid_where_void 这并不会对“工作单元”产生任何影响。由于使用await,您可以在最小结构更改的情况下将几乎所有算法都变成异步。如果您发布文章链接,我可以具体评论。 - usr
    2
    @valid_where_void:使用EF的工作单元模式与异步和同步无关。更多的是因为有一个新版本的EF,随之而来的是对模式和流程的新理解和建议。使用EF的工作单元/存储库从来都不是一个好主意,教程只是最终被更新以反映这一点。 - Chris Pratt
    2
    两件事情:1)说“永远”不会耗尽线程池是不准确的。即使您做了所有异步操作,仍然可以耗尽线程池。它只是给您比同步更高的上限。2)也许在.NET 4.5之前,同步代码的一个好处是“更简单的代码”,但是使用async/await后,异步操作变得非常简单,而且并不会使代码复杂化。 - Chris Pratt
    微软在Async方面的一个主要推动是针对移动设备上的Windows 8应用程序,该模型最大化UI响应性,同时最小化电池使用。但这些问题通常不在大多数Web开发人员的考虑范围内。话虽如此,异步对于仅在罕见事件(Signal R)上执行工作的长时间运行项(signal R);并行运行不同的部分或长时间运行的网络/数据库请求等情况起到了作用。 - Greg
    请查看以下两个链接,了解有关在ASP.NET 4.5及以上版本中使用异步方法的神奇之处以及一个重要的注意事项。同时,还可以了解一些关于异步编程的常识,可能会改变.NET程序员对它的认知。 - Greg
    显示剩余2条评论

    13

    在 ASP.NET 中,您应该使用异步API来处理任何与I/O相关的操作,包括数据库访问和 Web 服务调用。

    使用async允许 ASP.NET 充分利用线程池,从而获得非常好的可扩展性优势。


    5
    如Stephen所提到的,扩展性在线程池中。即使您网站的并非所有页面都依赖于数据库,使用异步调用也可以释放线程以服务请求。扩展性的好处绝对不是微不足道的。如果您想支持数十万个请求,当然需要查看您的数据库,但这不是他在这种情况下的意思。 - Wouter de Kort
    1
    @usr 这就是引发这个问题的原因,这是一个“如何”教程,他们正在向每个人灌输异步编程。我想知道这是否被认为是最佳实践,还是过度优化。我们已经使用同步调用多年了,对于99%的应用程序来说,这从未成为问题。 - public static
    2
    现代异步编程实际上与同步编程一样容易,而且在线程池耗尽之前就能带来好处:特别是,由于其受限的注入速率,async比线程池本身更快地扩展。因此,它非常适用于突发流量情况。 - Stephen Cleary
    1
    @usr:虽然真实情况是,如果你的网站每月只有10个访问量,异步可能确实是杀鸡焉用牛刀,但是想法是要从一开始就集成可扩展性。异步不是免费的,但它也不是那么昂贵。当你的网站突然在意料之外的高负载下崩溃(“Digg效应”)并因为你所有操作都是同步的原因而开始失去时,即使页面加载需要更长的毫秒数,所花费的时间也将远远超过自己的价值。 - Chris Pratt
    2
    @usr:你正在提出ASP.NET上异步数据库调用的经典案例。当异步操作很困难且只有一个SQL Server后端时,这个论点是有道理的。但现在,异步操作变得容易了,而后端更多地是Azure SQL或可扩展的NoSQL,这个论点就不那么站得住脚了(它仍然是一个有效的论点,只是现在弱了很多)。 - Stephen Cleary
    显示剩余5条评论

    4
    理想情况下,任何涉及等待时间的事情都应该异步完成。数据库查询通常必须向远程服务器发出调用,发送查询,然后等待服务器响应结果。这使得它成为异步操作的主要候选对象,因为整个“等待服务器响应”的部分是您无法在应用程序中解释的变量。
    使用异步允许Web服务器在代码等待异步操作完成时重用当前线程以处理其他Web请求。当它完成时,线程将返回给您的应用程序以继续处理。如果您使用同步,则在等待数据库或其他长时间运行的过程时,线程会死锁并且不能用于Web服务器池。如果您经常这样做,Web服务器可能会耗尽可用线程,并且必须开始排队进一步的请求。异步通过在某些情况下释放挂起线程来缓解这种情况,从而增加了Web服务器的潜在负载能力。

    1
    尽管你的回答很好,但你可能想指出不是“任何可能需要一段时间的事情都应该异步完成”。在 Web 应用程序中,应异步执行 I/O 任务,但不应执行 CPU 绑定工作。只有当您想要并行运行事物时,才可以拆分 CPU 绑定工作,但您仍应测量性能以确保您没有使用更多线程比值得的。 - Wouter de Kort
    没错,你不能异步处理CPU密集型工作,因为它需要线程来完成工作。然而,我试图保持答案简单化。在大多数情况下,CPU密集型工作基本上不会“花费很长时间”,除了复杂的科学或金融分析,这在Web场景中更加罕见(Web应用程序几乎肯定不会自己进行复杂的分析,而是与某些服务进行交互)。但是,你的观点仍然是有效的。 - Chris Pratt
    微软是否重新编写了所有库以使它们真正异步化?还是只是将其包装在异步块中,但在底层仍然是同步的? - public static
    我不知道全部。实际上,我只是遇到了一个非常烦人的事实,即在MVC5中仍然无法执行异步操作过滤器。但是,自从.NET 4.5引入了async/await组合以来,使核心库支持异步已成为优先事项。 - Chris Pratt
    @ChrisPratt:不幸的是,这是真的。然而,异步过滤器在WebApi上已经得到支持,并且将在ASP.NET vNext的MVC上得到支持。 - Stephen Cleary

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接