ASP.NET和异步 - 它是如何工作的?

Sam*_*ime 10 asp.net asp.net-mvc task-parallel-library async-await

我知道这是一个常见的问题,但我读过一篇文章并感到困惑.而现在我认为最好不要阅读它们)).

那么,ASP.NET如何工作(仅关于线程):

  1. http请求由线程池中的线程提供.
  2. 请求正在处理此线程正忙,因为请求正在处理这个线程内部.
  3. 当请求处理完成时,线程返回线程池,服务器发送响应.

这描述的行为是对的吗?

当我在ASP.NET MVC控制器中启动新任务时真正发生了什么?

public ActionResult Index()
{
    var task1 = Task.Factory.StartNew(() => DoSomeHeavyWork());
    return View();
}

private static async Task DoSomeHeavyWork()
{
    await Task.Delay(5000);
}
Run Code Online (Sandbox Code Playgroud)
  1. controller action开始在处理当前请求的线程内执行 - T1.
  2. 线程池为task1分配另一个线程(T2).
  3. task1在T2内"立即"启动.
  4. View结果返回"立即".
  5. ASP.NET做了一些工作,服务器发送响应,T1返回线程池,T2仍然活着.
  6. 在完成DoSomeHeavyWork的一段时间后,T2线程将返回到线程池.

这是对的吗 ?

现在让我们看看异步操作

public async Task<ActionResult> Index()
{
    await DoSomeHeavyWork();
    return View();
}
Run Code Online (Sandbox Code Playgroud)

,我理解与以前的代码示例的区别,但不是过程,在此示例中,行为如下:

  1. action开始在处理当前请求的线程内执行 - T1.
  2. DoSomeHeavyWork"立即"返回一个任务,让我们称它为"task1".
  3. T1返回线程池.
  4. 在DoSomeHeavyWork完成后,Index操作继续执行.
  5. 执行索引操作后,服务器将发送响应.

请解释第2点和第5点之间发生的事情,问题是:

  1. 在task1或其中(在哪里"等待")处理的DoSomeHeavyWork?我认为这是一个关键问题.
  2. 哪个线程将在等待之后继续处理请求 - 来自线程池的任何新线程,对吧?
  3. 请求生成从线程池分配的线程,但在DoSomeHeavyWorkAsync完成之前不会发送响应,并且此方法执行的线程无关紧要.换句话说,根据单个请求和单个具体任务(DoSomeHeavyWork),使用异步没有任何好处.这是对的吗 ?
  4. 如果前面的语句是正确的,那么我不明白异步如何能够改善具有相同单个任务的多个请求的性能.我会试着解释一下.我们假设线程池有50个线程可用于处理请求.单个请求应至少由线程池中的一个线程处理,如果请求启动另一个线程,则所有这些线程都将从线程池中获取,例如请求需要一个线程来处理自己,并行启动5个不同的任务并等待所有这些,线程池将有50 - 1 - 5 = 44个空闲线程来处理传入的请求 - 所以这是一个并行性,我们可以提高单个请求的性能,但我们减少了可以处理的请求数.因此,根据ASP.NET中的请求处理,我认为只有以某种方式启动IO完成线程的任务才能实现异步(TAP)的目标.但是在这种情况下IO完成线程如何回调线程池线程?

Yuv*_*kov 6

这描述的行为是对的吗?

是.

这是对的吗 ?

是.

在task1或其中(在哪里"等待")处理的DoSomeHeavyWork?我认为这是一个关键问题.

从当前代码,DoSomeHeavyWork将异步等待Task.Delay完成.是的,这将发生在线程池分配的同一个线程上,它不会旋转任何新线程.但是,并不能保证它将是同一个线程.

等待后哪个线程将继续处理请求?

因为我们讨论的是ASP.NET,所以它将是一个任意的线程池线程,并将其HttpContext封送到它上面.如果这是WinForms或WPF应用程序,那么在await您不使用之后,您将再次访问UI线程ConfigureAwait(false).

请求生成从线程池分配的线程,但在DoSomeHeavyWorkAsync完成之前不会发送响应,并且此方法执行的线程无关紧要.换句话说,根据单个请求和单个具体任务(DoSomeHeavyWork),使用异步没有任何好处.这是对的吗 ?

在这种特殊情况下,您将看不到异步的好处.当你有并发请求命中服务器时,异步会闪耀,而且很多都在进行IO绑定工作.例如,当您在访问数据库时使用异步时,您可以在查询执行的时间内释放线程池线程,从而允许同一个线程同时处理更多请求.

但是在这种情况下IO完成线程如何回调线程池线程?

您必须将并行性和并发性分开.如果您需要计算能力来并行执行CPU绑定工作,则异步不是实现它的工具.另一方面,如果您有许多并发 IO绑定操作,例如命中数据库以进行CRUD操作,则可以通过在执行IO操作时释放线程来使用async .这是异步的主要关键点.

线程池具有专用的IO完成线程池以及工作线程,您可以通过调用来查看它们ThreadPool.GetAvailableThreads.使用IO绑定操作时,检索回调的线程通常是IO完成线程,而不是工作线程.他们都有不同的游泳池.

  • @SamousPrime你[不应该阻止异步代码](http://blog.stephencleary.com/2012/07/dont-block-on-async-code.html),它可以引导你遇到各种各样的问题,包括死锁.使用`await`.如果您不确定代码在做什么,您可以随时使用.NET反编译器查看它. (2认同)