当我第一次看到 async 和 await 时,我认为它们是异步编程模型的 C# 语法糖。我错了,async 和 await 不止于此。这是一个全新的异步模式基于任务的异步模式,http://www.microsoft.com/en-us/download/details.aspx?id=19957 是一篇很好的入门文章。大多数实现 TAP 的 FCL 类都是调用 APM 方法(BegingXXX() 和 EndXXX())。以下是 TAP 和 AMP 的两个代码快照:
TAP 示例:
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 文章“Asynchronous Pages in ASP.NET 2.0”,它非常详细地解释了旧的异步在 ASP.NET 2.0 中如何工作。
关于异步编程模型,请参阅 Jeffrey Richter 的文章“Implementing the CLR Asynchronous Programming Model”了解更多详情,也可参阅他的书“CLR via Csharp 3rd Edition”第 27 章。