IActionResult 或 async Task<IActionResult> 有啥好处?

Posted

技术标签:

【中文标题】IActionResult 或 async Task<IActionResult> 有啥好处?【英文标题】:IActionResult or async Task<IActionResult> what are the advantages?IActionResult 或 async Task<IActionResult> 有什么好处? 【发布时间】:2019-07-30 12:18:50 【问题描述】:

我们将在 dotnet core 2.1 中创建一个新的 API,这个 Web API 将是一个高流量/事务,例如每分钟 10,000 或更高。我通常像下面这样创建我的 API。

[HttpGet("some")]
public IActionResult SomeTask(int id)

   var result = _repository.GetData(id)
   return Ok(result);

如果我们像下面这样实现我们的 Web API,会有什么好处?

[HttpGet("some")]
public async Task<IActionResult> SomeTask(int id)

    await result = _repository.GetData(id);
    return Ok(result);

我们还将为这个新 API 使用 EF 核心,如果我们执行异步任务,我们是否也应该使用 EF 异步

【问题讨论】:

好吧,你不会从唯一的awaitawait Task.CompletedTask; 的异步方法中获得任何好处——因为它会轻而易举地越过那条线而不会导致任何“异步”发生。 此链接解释了异步的含义。 ***.com/questions/16083613/… 对不起,这将是从数据库中获取数据的东西。我会更新我的问题 @Chris: When dealing with I/O on ASP.NET, async helps you make maximum use of the thread pool 【参考方案1】:

您真正要问的是同步和异步之间的区别。在非常基本术语中,异步允许线程切换的可能性,即工作在一个线程上开始,但在另一个线程上结束,而同步保持在同一个线程上。

如果没有特定应用程序中正在发生的事情的上下文,这本身并没有多大意义。对于 Web 应用程序,您有一个线程池。线程池通常由 1000 个线程组成,因为这是跨 Web 服务器的典型默认设置。这个数字可以更少或更多;这对这个讨论并不重要。不过,重要的是要注意,池中的最大线程数有一个非常实际的物理限制。因为每一个都会消耗一些系统资源。

因此,这个线程池通常也被称为“最大请求数”,因为一般来说一个请求 = 一个线程。因此,如果你有 1000 个线程池,理论上可以同时服务 1000 个请求。任何超过该队列的内容都会在其中一个线程可用后进行处理。这就是异步的用武之地。

异步工作几乎是 I/O 工作:查询数据库、读取/写入文件系统、向其他服务(例如 API)发出请求等。所有这些工作通常都会有一段时间的空闲时间。例如,对于数据库查询,您进行查询,然后等待。查询需要一些时间才能到达数据库服务器,数据库服务器处理它并生成结果集,然后数据库服务器将结果发回。异步允许在这些期间将活动线程返回到池中,然后它可以在其中为其他请求提供服务。因此,假设您有这样的操作正在进行数据库查询,如果它是同步的并且您收到了 1001 个对该操作的同时请求,则前 1000 个将开始处理,最后一个将成为队列。在其他 1000 个请求中的一个完全完成之前,无法处理最后一个请求。然而,使用异步,一旦千人中的一个将查询交给数据库服务器,它就可以返回到线程池以处理该等待请求。

这都是有点高级。实际上,有一个 lot 涉及到这一点,这真的不是那么简单。 Async 不保证线程会被释放。某些工作,特别是受 CPU 限制的工作,永远不可能是异步的,所以即使你在异步方法中执行它,它也会像同步一样运行。但是,一般来说,在线程匮乏的情况下,异步将处理比同步更多的请求。不过,这确实是有代价的。在线程之间切换的额外工作增加了一些开销,即使它是微不足道的,所以异步几乎总是比同步慢,即使只有纳秒。但是,异步是关于规模,而不是性能,而且性能损失通常是可以接受的交易,以提高扩展能力。

【讨论】:

但是,对于 WebApi,如果已经有 1000 个请求正在访问数据库,这并不意味着已经有 1000 个线程在等待这些请求。您不会像使每个 IActionResult 方法异步一样通过增加 1000 限制(无论设置在哪里)来完成同样的事情吗?如果同时请求已经有 1000 个限制,那可能是有原因的。通过使用异步调用来允许更多请求会不会对此产生影响? 正如我在回答中所说,线程池的大小有一个硬性限制。每个线程,无论是否处于活动状态,都会消耗系统资源。是的,您可以获得一些具有大量 RAM 和计算能力的巨型服务器,而不必担心异步,但这很昂贵,尤其是在您按使用付费的云场景中。异步是为了更有效地使用现有资源。 但我的观点是,将异步添加到 WEBAPI 请求似乎没有任何作用,因为它们已经是异步的。这是真的还是假的? 嗯? Web 请求本质上是一个异步过程,但这与正在执行的操作是否异步无关。您可以选择向 API 同步或异步发出请求,API 所做的事情可以是同步或异步的。 API 不仅仅是因为它是 API 而“异步”。 阅读文档和其他一些文章,我想我找到了我困惑的答案。在 Asp.Net 中,您假设的“1 个请求 = 1 个线程”是不正确的。对于每个请求,仅在同一个 Asp.Net 线程上进行上下文切换。因此,对于 1000 个请求,没有 1000 个线程已经在运行。因此,在 OP 的情况下,如果他正在执行 DB 调用,则使用 async 是有益的。

以上是关于IActionResult 或 async Task<IActionResult> 有啥好处?的主要内容,如果未能解决你的问题,请参考以下文章

.Net Core:从自定义异常中间件返回 IActionResult

深入了解异步async/await,为啥这种异步的性能这么高?异步的原理,本文彻底来个说明

IActionResult的返回类型

IActionResult 类型返回结果时如何在 Xunit 中获取内容值

Azure Functions - 返回类型 HttpResponseMessage 和 IActionResult 之间的区别

将开发人员异常页面显示为 IActionResult