第一次看到async和await时,我认为它们是异步编程模型的 C# 语法糖。我错了,async和await不止于此。这是一个全新的异步模式基于任务的异步模式,http://www.microsoft.com/en-us/download/details.aspx?id=19957是一篇不错的入门文章。大多数实现 TAP 的 FCL 类都是调用 APM 方法(BegingXXX() 和 EndXXX())。以下是 TAP 和 AMP 的两个代码快照:
水龙头样品:
static void Main(string[] args)
{
GetResponse();
Console.ReadLine();
}
private static async Task<WebResponse> GetResponse()
{
var webRequest = WebRequest.Create("http://www.google.com");
Task<WebResponse> response = webRequest.GetResponseAsync();
Console.WriteLine(new StreamReader(response.Result.GetResponseStream()).ReadToEnd());
return response.Result;
}
APM 样本:
static void Main(string[] args)
{
var webRequest = WebRequest.Create("http://www.google.com");
webRequest.BeginGetResponse(EndResponse, webRequest);
Console.ReadLine();
}
static void EndResponse(IAsyncResult result)
{
var webRequest = (WebRequest) result.AsyncState;
var response = webRequest.EndGetResponse(result);
Console.WriteLine(new StreamReader(response.GetResponseStream()).ReadToEnd());
}
最后这两个会是一样的,因为 GetResponseAsync() 在里面调用了 BeginGetResponse() 和 EndGetResponse()。当我们反射 GetResponseAsync() 的源代码时,我们会得到这样的代码:
task = Task<WebResponse>.Factory.FromAsync(
new Func<AsyncCallback, object, IAsyncResult>(this.BeginGetResponse),
new Func<IAsyncResult, WebResponse>(this.EndGetResponse), null);
对于 APM,在 BeginXXX() 中,有一个回调方法的参数,该方法将在任务(通常是 IO 繁重的操作)完成时调用。创建一个新线程并且是异步的,它们都会立即在主线程中返回,它们都是未阻塞的。在性能方面,在处理 I/O 绑定操作(例如读取文件、数据库操作和网络读取)时,创建新线程将消耗更多资源。创建新线程有两个缺点,
- 就像在您提到的文章中一样,内存成本和 CLR 是
线程池的限制。
- 会发生上下文切换。另一方面,异步不会手动创建任何线程,也不会在 IO 绑定操作返回时进行上下文切换。
这是一张有助于理解差异的图片:
此图来自 MSDN 文章“ ASP.NET 2.0 中的异步页面”,其中非常详细地解释了旧的异步如何在 ASP.NET 2.0 中工作。
关于异步编程模型,请从 Jeffrey Richter 的文章“实现 CLR 异步编程模型”中获得更多详细信息,在他的书“CLR via Csharp 3rd Edition”第 27 章中也有更多详细信息。