使控制器方法异步可以提高其性能吗?有什么好处?

时间:2019-05-03 09:36:32

标签: c# rest asynchronous api-design

免责声明: 我已经仔细考虑了此问题是否基于观点,因为我了解遵守站点规则的重要性。我的结论是,它足够具体,可以在这里回答。如果我能纠正我的歉意。

我选择编写没有 async Task 的控制器。按照我的想法,由于两个原因,使操作同步没有任何好处。

  1. 立即提供整个视图模型,在准备好呈现整个内容之前(即不逐步传输列表),没有有意义的信息要呈现。

  2. 该服务中的操作需要进行一些繁重的数据处理,并且基于逻辑,该逻辑假定所有已检索元素的知识,并在公开之前存储某些信息(即没有 SaveChangesAsync() 或类似的方法,并且可以使用 AsNoTracking())。

描述视图模型的类大致如此。

public class TheViewModel
{
  public string Name { get; set; }
  public byte[] Pieces { get; set; }
  public Guid Id { get; set; }
  public Dictionary<int, Info> { get; set; }
}

public class Info
{
  public Guid Id { get; set; }
  public Guid ParentLeftId { get; set; }
  public Guid ParentRightId { get; set; }
  public Guid ParentRandId { get; set; }
  public int[] Data { get; set; }
  public List<DateTime> { get; set; }
}

我的一位同事对我的选择做出了回应,并争辩说:

a。这不是推荐的方法, b。操作效率会降低, C。这违反了最佳实践。

我们用Google搜索并阅读了很多博客。总体印象表明该参数有效。但是,我对它与我们的案例的相关性表示怀疑。 Prestanda不是民主问题,不能基于观点。

当我尝试查找表示性能到底会如何受到影响的实际参考(MSDN,SO等)时,我得到的很少。我也没有找到太多有关如何可靠测试的信息。

2 个答案:

答案 0 :(得分:3)

  

立即提供整个视图模型,在准备好呈现整个内容之前,没有有意义的信息可供呈现

这实际上不是问题。

在某个地方,您很可能从数据库中获取信息(您提到了保存更改。)如果您与数据库进行异步交互(例如ExecuteDataReaderAsync),则等待它意味着在查询执行线程时执行查询的操作可以执行其他操作。当查询完成执行后,该线程(或另一个线程,没关系)将完成。

允许线程在查询时执行其他操作,而不是只等待等待,从而提高了应用程序的性能。

我在这里感到困惑:async/await如何使您的控制器方法执行得更快? 不是。一点也不。实际上,在这样的任务之间切换线程可能会使速度变慢。 (这通常很琐碎,您不必为此担心。)

但是总的来说,您的应用程序的性能会更快,因为无论有多少线程可用-即使只有一个线程-都有机会让他们做一些事情而不是等待。

您正在执行的个别操作(例如执行控制器方法)并没有更快,但是您为应用程序提供了一个机会,可以利用它拥有的每个线程做更多的事情。无论花多长时间,它都可能会更快启动,因为它没有在等待线程,这意味着它完成得更快,而不是更快。

即使您的应用程序一次只收到一个请求并仅使用一个线程处理该请求,也存在以下情况:如果您发出两个都不依赖于另一个结果的数据库或API请求,该怎么办?如果您同步处理它们,则将等待一个完成,启动下一个,然后等待下一个完成。如果以异步方式处理它们,则启动一个,而在等待时可以启动下一个。两项任务都不会更快地完成。区别在于第二个任务较早开始-而第一个任务与其他无关,只是等待。

因此,当我们谈论“线程”时,它并不是真正意义上的多个线程。关于每个线程的使用方式。

答案 1 :(得分:1)

我将尝试以另一种方式解释这一点。

异步/等待通常有助于IO操作。这些包括(但不限于):访问文件,访问数据库,调用Web服务,使用网络套接字进行操作,甚至访问某些特殊硬件(如扫描仪)。基本上,在某些时间点上,您的代码需要等待来进行某些外部操作的结果-文件读取操作,SQL查询,HTTP请求等等。

这就是异步/等待帮助。没有它,您的线程将被卡在等待结果,这通常需要很长时间。但是有了它,您的线程可以在等待时切换为执行其他操作。因此,可以更有效地使用您的CPU。

注意-这不是多线程。没有“实际完成工作”的其他线程。取而代之的是,在所有抽象层的最底层,计算机正在等待某些硬件报告,包括硬盘驱动器,网络适配器等等。 (嗯,是的,您可以也可以等待另一个线程完成其正在执行的操作,但这是一种不常见的情况)

在您的方案中,使用async / await可以通过单个线程处理两个传入的REST请求。您将从第一个开始,然后到需要等待的地步(例如,因为它正在执行SQL查询),然后ASP.NET将切换到执行第二个。然后,当第二个要等待(或可能完成)时,ASP.NET将检查SQL结果是否在其中,并且可以继续使用第一个。您的线程永远不会同时处理多个请求,但是它将在它们之间来回切换-每当一个请求等待时,它将继续执行另一个请求。这样您就可以提高效率。

请记住,如果您的请求在CPU上进行计算所需的时间比在等待某些外部IO上花费的时间多,那么从异步/等待中获得的收益就会更少。而且,如果您根本不做IO(例如,结果仅取决于请求的内容),那么您实际上就不需要任何异步/等待,因为没有什么可等待的。

即使您确实找到了用于异步/等待的方法,它也无法更快地帮助处理单个请求-它只会允许您同时处理更多请求。