在这种情况下,Task
、async
和await
非常出色。下面是同一个示例的重构版本,充分利用了async
(它还使用了我AsyncEx库中的一些辅助类来清理映射代码):
public abstract class HttpAsyncHandlerBase : IHttpAsyncHandler
{
public abstract Task ProcessRequestAsync(HttpContext context);
IAsyncResult IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData)
{
var task = ProcessRequestAsync(context);
return Nito.AsyncEx.AsyncFactory.ToBegin(task, cb, extraData);
}
void EndProcessRequest(IAsyncResult result)
{
Nito.AsyncEx.AsyncFactory.ToEnd(result);
}
void ProcessRequest(HttpContext context)
{
EndProcessRequest(BeginProcessRequest(context, null, null));
}
public virtual bool IsReusable
{
get { return true; }
}
}
public class MyAsyncHandler : HttpAsyncHandlerBase
{
public override async Task ProcessRequestAsync(HttpContext context)
{
using (var webClient = new WebClient())
{
var data = await webClient.DownloadDataTaskAsync("http://my resource");
context.Response.ContentType = "text/xml";
context.Response.OutputStream.Write(data, 0, data.Length);
}
}
}
(正如代码中所述,.NET 4.5有一个HttpTaskAsyncHandler
与我们上面的HttpAsyncHandlerBase
类似)。
async
真正酷的事情在于它在执行后台操作时不需要任何线程:
- 一个ASP.NET请求线程启动请求并使用
WebClient
开始下载。
- 当下载进行时,
await
实际上会从async
方法中返回,使得请求线程退出,然后将该请求线程归还给线程池 - 这样请求时就有0个(零)线程服务了。
- 当下载完成时,
async
方法将在一个请求线程上恢复。该请求线程仅短暂地用于编写实际响应。
这是最佳的线程解决方案(因为需要一个请求线程来编写响应)。
原始示例还以最佳方式使用线程 - 就线程而言,它与基于async
的代码相同。但是我认为async
代码更容易阅读。
如果您想了解更多关于async
的内容,我在我的博客中有一篇介绍文章。