4

在书临ASP.NET MVC 4有一个异步操作的例子:ASP.NET MVC什么线程池用于自定义任务?

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调用此方法并在我的操作中等待不会导致可以同时处理更多的请求,因为I/O绑定方法的任务不再可用于请求处理。还是有一个单独的线程池只为请求和使用Task.Run中的动作自动使用不同的?我想到的是这个问题:Using ThreadPool.QueueUserWorkItem in ASP.NET in a high traffic scenario答案或多或少只有来自库的异步方法应该被使用,这些库使用自己的线程池。

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

回答

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.StartNewTask.Run

相反,Task.Delay是自然异步操作。对于“自然异步”,我的意思是异步,就像I/O操作是异步的(例如,用于Web调用的HttpClient)一样。自然异步操作不使用线程,因此它们对ASP.NET服务器的吸引力(降低线程池的压力,使您可以扩展更多)。

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

在旁注中,AsyncController是来自MVC3的剩余物。它不需要async/await

+0

我相信你在这里是故意的简单化,但我认为这意味着没有线程被使用是有点危险的。没有*线程池线程正在被使用,但取决于正在完成的工作,系统上可能还有其他线程正在使用。 'Task.Delay',具体来说不会需要一个线程,但还有其他的东西可能。然而,从网络应用的角度来看,是的,这将是“无线”的工作,所以没有错,只是不完全准确。 –

+0

@ChrisPratt:我的意思是没有线程正在使用该请求。在系统(和应用程序)的其他地方有各种线程在做事情,如果你阻塞了请求线程,那些相同的线程仍然会在那里做同样的事情。 即使'Task.Delay':BCL实际上有一个负责所有管理定时器的“定时器线程”,所以有一个用于定时器的BCL线程。但它是整个AppDomain中的一个线程,而不是每个定时器。同样的原则在这里:我们正在避免每个请求都需要额外线程的情况。 –