异步编程模式的英文全称是The Asynchronous Programming Models,简称是APM。简单说明一下为什么要异步编程,以及异步编程带来的好处有:
1. 快速响应的用户界面
对于用户界面而言,它的响应用户的能力是非常关键的。如果耗时的操作阻塞了UI线程,造成UI线程不能响应用户操作。用户就会抛弃我们的系统。所以我们需要一种机制,在发起耗时操作的请求之后要立即返回,不要阻塞UI线程,让UI线程可以继续响应用用户的操作。然后等耗时操作返回后,通过回调来处理耗时操作返回的结果。
2. 更高的伸缩性
在服务端应用中,有非常多的IO操作:数据库访问,磁盘操作,Socket访问等。对于这些IO操作,单独占用一个线程来同步处理,浪费服务器的资源,使用IOCP异步方式可以有效解决这种问题,关于IOCP的具体信息,可以阅读本订阅号之前的文章。
所以我们需要掌握异步编程的技能。在.Net Framework中,可以实现异步编程的方式有很多种,今天我们主要分析四种异步的方式:
1. The Standard APM
2. The Event-based APM
3. The Task-based APM
4. The Await Async APM
一、The Standard APM
在FCL提供了具有BeginXxx和EndXxx方法的各种类型,一般这种命名的方法,就提供了Standard APM的支持。通常BeginXxx方法具有Xxx方法相同的参数以外,还有两个附加参数:callback和stateObject。callback就是异步的回调方法,它需要接受一个IAsyncResult类型的参数,然后在回调方法中访问它的AsyncState属性就可以得到stateObject的值。示例代码:
代码语言:javascript复制class StandardAPM
{
public static void Test()
{
Console.WriteLine("1. Begin");
WebRequest request = HttpWebRequest.Create("http://www.baidu.com");
request.BeginGetResponse(DoCallback, request);
Console.WriteLine("2. Sync continue");
}
static void DoCallback(IAsyncResult ar)
{
WebRequest request = (WebRequest)ar.AsyncState;
WebResponse response = request.EndGetResponse(ar);
request.Abort();
response.Close();
Console.WriteLine("3. Async callback");
}
}
通过使用Standard APM,您的代码变得更加复杂,代码逻辑不清楚。
二、The Event-based APM
框架类库(FCL)还附带了一些支持基于事件的APM的类型。例如,在使用System.Net.WebClient类的时候,通过调用DownloadDataAsync方法,并且把回调方法订阅在DownloadDataCompleted事件上,可以帮助我们达到异步效果。程序开始异步操作从指定的URL下载数据,当它完成时,将触发DownloadDataCompleted事件。示例代码:
代码语言:javascript复制class EventBasedAPM
{
public static void Test()
{
WebClient wc = new WebClient();
wc.DownloadDataCompleted = DoCompleted;
Console.WriteLine("1. Begin");
wc.DownloadDataAsync(new Uri("https://www.baidu.com"));
Console.WriteLine("2. Sync continue");
}
public static void DoCompleted(object sender
, DownloadDataCompletedEventArgs e)
{
Console.WriteLine("3. Async callback");
}
}
实际上它的作用与使用BeginXxx和EndXxx方法相同,区别在于基于事件的APM更接近对象模型层,但是FCL支持基于事件APM的类型非常少,个人建议尽可能不要使用这种模式。
三、The Task-based APM
.NETFramework4.0引入了用于并行计算和异步编程的新任务并行库(TPL)。在System.Threading.Tasks命名空间中定义的主要使用的Task类表示要完成的用户工作项,要使用基于任务的APM,您必须创建Task的新实例,或者Task<T>类,传递Action或Action<T>委托的实例作为Task或Task<T>构造函数的第一个参数,然后调用Task的实例方法Start,通知任务调度程序尽快安排此任务。示例代码:
代码语言:javascript复制class TaskBasedAPM
{
public static void Test()
{
Console.WriteLine("1. Begin");
DoAsync();
Console.WriteLine("2. Sync continue");
}
static void DoAsync()
{
WebRequest request = HttpWebRequest.Create("http://www.baidu.com");
Task<WebResponse> task = request.GetResponseAsync();
task.ContinueWith(t => {
request.Abort();
t.Result.Close();
Console.WriteLine("3. Async callback");
});
}
}
四、The Await Async APM
在C# 5.0中引入了async和await关键字,它们是异步编码的语法糖,在C#编译器进行编译之后,能够达到异步的效果,写出来的代码更加接近同步代码,逻辑更加清楚,易于阅读。它的异步原理是和Task-based APM一样的。示例代码:
代码语言:javascript复制class AwaitAsyncAPM
{
public static void Test()
{
Console.WriteLine("1. Begin");
DoAsync();
Console.WriteLine("2. Sync continue");
}
static async Task DoAsync()
{
WebRequest request = HttpWebRequest.Create("http://www.baidu.com");
WebResponse webResponse = await request.GetResponseAsync();
request.Abort();
webResponse.Close();
Console.WriteLine("3. Async callback");
//使用await和async关键字定义的异步方法,可以return返回值也可以没有。
//有没有return语句的时候,异步方法返回的是Task
//有return语句的时候,异步方法返回的是Task<ResultType>
//当调用返回的task.Result的时候,线程会阻塞,并等待异步方法真实返回结果。
}
}
最后在这四种方式进行总结:
The Standard APM: 是基于线程池实现的,可以广泛使用,标准,推荐,支持取消和延续。
The Event-based APM: 是基于线程池实现的,尽量避免使用,不建议使用。
The Task-based APM: 是指定的任务调度程序,推荐,支持线程池模式的所有功能,并具有许多其他功能。
The Await Async APM: 是基于Task-based APM的模式,新的C#5.0异步模式,推荐使用。