为什么我应该在控制器中返回 Task<IActionResult>?

Zes*_*Rex 10 .net c# asp.net .net-core

因此,我已经尝试掌握相当长一段时间了,但看不到将每个控制器端点声明为异步方法的意义。

让我们看一下 GET-Request 来形象化这个问题。

这是我处理简单请求的方法,只需完成工作并发送响应即可。

[HttpGet]
public IActionResult GetUsers()
{
      // Do some work and get a list of all user-objects.
      List<User> userList = _dbService.ReadAllUsers(); 
      return Ok(userList);
} 
Run Code Online (Sandbox Code Playgroud)

下面是async Task<IActionResult>我经常看到的选项,它与上面的方法相同,但方法本身返回一个Task. 人们可能会认为,这种方法更好,因为您可以收到多个请求,并且它们可以异步处理,但我测试了这种方法和上面的方法,得到了相同的结果。两者都可以同时处理多个请求。那么我为什么要选择这个签名而不是上面的签名呢?我只能看到这样做的负面影响,例如由于异步而将代码转换为状态机。

[HttpGet]
public async Task<IActionResult> GetUsers()
{
      // Do some work and get a list of all user-objects.
      List<User> userList = _dbService.ReadAllUsers(); 
      return Ok(userList);
} 
Run Code Online (Sandbox Code Playgroud)

下面的这种方法也是我无法理解的。我看到很多代码都具有这种设置。他们使用一种async方法await,然后返回结果。这样的等待会使代码再次顺序化,而不是享受多任务/多线程的好处。我在这一点上有错吗?

[HttpGet]
public async Task<IActionResult> GetUsers()
{
      // Do some work and get a list of all user-objects.
      List<User> userList = await _dbService.ReadAllUsersAsync(); 
      return Ok(userList);
} 
Run Code Online (Sandbox Code Playgroud)

如果您能用事实启发我,那就太好了,这样我就可以像现在一样继续发展,或者知道我由于误解了这个概念而一直做错了。

Ste*_*ary 10

请阅读ASP.NET 上的 Async/Await 简介的“同步与异步请求处理”部分

\n
\n

两者都可以同时处理多个请求。

\n
\n

是的。这是因为 ASP.NET 是多线程的。因此,在同步情况下,您只需有多个线程调用相同的操作方法(在不同的控制器实例上)。

\n

对于非多线程平台(例如,Node.js),您必须使代码异步才能处理同一进程中的多个请求。但在 ASP.NET 上它是可选的。

\n
\n

这样的等待会使代码再次顺序化,而不是享受多任务/多线程的好处。

\n
\n

是的,它是顺序的,但它不是同步的。它是顺序的,因为该async方法一次执行一个语句,并且该请求在该async方法完成之前才完成。但它不是同步的- 同步代码也是顺序的,但它会阻塞线程,直到方法完成。

\n
\n

那么我为什么要选择这个签名而不是上面的签名呢?

\n
\n

如果您的后端可以扩展,那么异步操作方法的好处就是可扩展性。具体来说,异步操作方法在异步操作正在进行时产生其线程 - 在这种情况下,GetUsers在数据库执行查询时不占用线程。

\n

在测试环境中很难看到这种好处,因为你的服务器有空闲线程,所以调用异步方法 10 次(占用 0 个线程)和调用同步方法 10 次(占用 0 个线程)之间没有明显的区别。最多 10 个线程,还有 54 个备用)。您可以人为地限制 ASP.NET 服务器中的线程数量,然后进行一些测试来查看差异。

\n

在现实世界的服务器中,您通常希望使其尽可能异步,以便您的线程可用于处理其他请求。或者,如下所述

\n
\n

请记住,异步代码不会取代线程池。这是\xe2\x80\x99t线程池异步代码;it\xe2\x80\x99s线程池异步代码。异步代码允许您的应用程序充分利用线程池。它使用现有的线程池并将其增加到 11。

\n
\n

记住上面的“如果”;这尤其适用于现有代码。如果您只有一个 SQL Server 后端,并且几乎所有操作都查询数据库,那么将它们更改为异步可能没有用,因为可扩展性瓶颈通常是数据库服务器而不是 Web 服务器。但是,如果您的 Web 应用程序可以使用线程来处理非数据库请求,或者您的数据库后端是可扩展的(NoSQL、SQL Azure 等),那么将操作方法​​更改为异步可能会有所帮助。

\n

对于新代码,我默认推荐异步方法。异步可以更好地利用服务器资源,并且对云更加友好(即,即用即付托管的成本更低)。

\n