当我第一次看到async和await时,我认为它们是异步编程模型的C#语法糖。但我错了,async和await不仅仅是如此。它是一种全新的基于任务的异步模式(Task-based Asynchronous Pattern),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对线程池的限制。
- 将发生上下文切换。另一方面,异步不会手动创建任何线程,并且当I/O绑定操作返回时不会进行上下文切换。
这里有一张图片可以帮助理解差异:
![enter image description here](https://istack.dev59.com/q3xU7.gif)
这张图来自 MSDN 文章 "
ASP.NET 2.0 中的异步页面", 详细解释了 ASP.NET 2.0 中旧的异步工作方式。
关于异步编程模型,请参考 Jeffrey Richter 的文章 "
实现 CLR 异步编程模型",他的书 "CLR via C# 3rd Edition" 第27章也有更多细节。
ConfigureAwait(false)
是最佳实践,因此这实际上是每秒100次UI更新-比人类可以处理的要多得多。 - Stephen Cleary