4

在 Pro ASP.NET MVC 4 一书中,有一个异步操作的示例:

public class RemoteDataController : AsyncController
{
 public async Task<ActionResult> ConsumeAsyncMethod() {
    string data = await new RemoteService().GetRemoteDataAsync();
    return View("Data", (object)data);
 }
}


public class RemoteService 
{
  public async Task<string> GetRemoteDataAsync() {
    return await Task<string>.Factory.StartNew(() => {
            Thread.Sleep(2000);
            return "Hello from the other side of the world";
        });
    }
}

我的问题是:该任务是否不仅使用线程池中的一个线程,该线程也用于服务请求?

假设我有一个同步 I/O 绑定方法。我认为在我的操作中使用 Task.Run 和 await 调用此方法不会导致更多可以同时处理的请求,因为 I/O 绑定方法的任务不再可用于请求处理。或者是否有一个单独的线程池仅用于请求并且在操作中使用 Task.Run 会自动使用不同的线程池?让我想到的是这个问题:在高流量场景中使用 ASP.NET 中的 ThreadPool.QueueUserWorkItem,答案或多或少应该只使用库中的异步方法,而这些库使用自己的线程池。

是否可以配置行为?它与 ASP.NET WebForms 的工作方式相同吗?

4

1 回答 1

4

例子

这是一个非常糟糕的例子。我立即发现三件事是错误的,但主要的是您指出的:

该任务是否不只是使用线程池中的一个线程,该线程也用于服务请求?

是的,那个例子会。

请考虑这个例子:

public class RemoteDataController : Controller
{
  public async Task<ActionResult> ConsumeAsyncMethod() {
    string data = await new RemoteService().GetRemoteDataAsync();
    return View("Data", data);
  }
}

public class RemoteService 
{
  public async Task<string> GetRemoteDataAsync() {
    await Task.Delay(2000);
    return "Hello from the other side of the world";
  }
}

原始示例使用Thread.Sleep. 这在 ASP.NET 上完全适得其反。作为一般规则,不要在 ASP.NET 上使用Task.Factory.StartNew或。Task.Run

相反,Task.Delay是一个自然异步的操作。“自然异步”是指异步,就像 I/O 操作是异步的一样(例如,HttpClient对于 Web 调用)。自然异步操作不使用线程,因此它们对 ASP.NET 服务器具有吸引力(减少线程池的压力,允许您进行更多扩展)。

想想它是如何工作的很有趣:当你像我的例子一样使用自然异步方法时,一个线程启动请求直到它到达await; 此时,请求线程返回到线程池(!),在接下来的两秒钟内,没有线程处理该请求(但请求尚未完成)。我喜欢将这种现象称为“零线程并发”。完成Delay后,线程继续处理请求并完成它。

附带说明一下,AsyncController是 MVC3 的遗留物。async/不需要它await

于 2013-10-30T22:42:01.523 回答