一、什么是异步编程?编程
异步编程就是把耗时的操做放进一个单独的线程中进行处理(该线程须要将执行进度反映到界面上)。因为耗时操做是在另一个线程中被执行的,因此它不会堵塞主线程。主线程开启这些单独的线程后,还能够继续执行其余操做(例如窗体绘制等)。异步
异步编程能够提升用户体验,避免在进行耗时操做时让用户看到程序“卡死”的现象。async
二、异步编程模型(APM)ide
APM是Asynchronous Programming Mode的缩写,即异步编程模型的意思,它容许程序用更少的线程去执行更多的操做。在.NET Framework中,要分辨某个类是否实现了异步编程模型,主要就是看该类是否实现了返回类型为IAsyncResult接口的BeginXXX方法和EndXXX方法。异步编程
因为委托类型定义了BeginInvoke和EndInvoke方法,因此委托类型都实现了异步编程模型。spa
2.1 Beginxxx方法--开始执行异步操做线程
在须要获取文件中的内容时,咱们一般会使用FileStream的同步方法Read进行读取,该同步方法的定义为:code
public override int Read(byte[] array,int offset,int count)对象
当使用上面的方法读取大文件的内容时,会出现堵塞UI线程,致使在文件内容没有读取完成以前,用户不能对窗体进行任何操做(包括关闭应用程序),这时窗体就会出现没法响应的状况。blog
为了解决这个问题,微软早在.NET 1.0的时候就提出了异步编程模型,并为FileStream类提供了异步模式的方法实现,即BeginRead方法。该方法会异步地执行读取操做,并返回实现了IAsyncResult接口的对象(该对象存储这异步操做的信息)。
下面给出了BeginRead方法的定义,咱们能够从中找出它与同步方法Read的区别:
public override IAsyncResult BeginRead(byte[] array,int offset,int numBytes,AsyncCallback userCallback,Object stateObject)
从以上的异步方法的定义能够看出,该异步方法的前面3个参数与同步方法Read一致,后两个参数userCallback和StateObject则是同步方法所不具有的。userCallback表示异步操做完成后须要回调的方法,该方法必须匹配AsyncCallback委托类型;stateObject则表明传递给回调方法的对象,在回调方法中,能够经过查询IAsyncResult接口的AsyncState属性来读取该对象。该异步方法之因此不会堵塞UI线程,是由于它在被调用后,会当即把控制权交还给调用线程(若是是UI线程调用了该方法,则就将控制权返回给UI线程),而后由另外一个线程去执行文件读取操做。
2.2 Endxxx方法--结束异步操做
每次调用Beginxxx方法后,应用程序还需调用Endxxx方法来获取操做返回的结果。Beginxxx方法所返回的,是实现了IAsyncResult接口的对象,该对象并不是相应的同步方法返回的结果。此时还须要调用Endxxx方法来结束异步操做,并向该方法传递Beginxxx所返回的对象。Endxxx方法返回的类型与同步方法相同,如FileStream的EndRead方法会返回一个Int32类型,表明从文件流中实际读取的字节数。
Endxxx方法有许多中方式调用,但有一种是最经常使用的,即便用AsyncCallback委托来指定操做完成时要调用的方法,在回调方法中调用Endxxx方法来得到异步操做返回的结果。
1 static void Main() 2 { 3 SynchronizationContext sc=SynchronizationContext.Current; 4 AsyncMethodCaller methodCaller=new AsyncMethodCaller(DownLoadFileAsync); 5 method.BeginInvoke(txtUrl.Text.Trim(),GetResult,null); 6 } 7 private static void GetRsult(IAsyncResult result) 8 { 9 AsyncMethodCaller caller=(AsyncMethodCaller)((AsyncResult)result).AsyncDelegate; 10 string returnstring=call.EndInvoke(result); 11 }
三、异步编程模型(EAP)
虽然前面的异步编程能够解决执行耗时操做时界面没法响应的问题,但APM也一样存在这一些明显的问题,如不支持对异步操做的取消以及不能提供下载进度报告等。然而对于桌面应用程序而言,进度报告和取消操做的功能是必不可少的,因此微软在.NET 2.0 发布时又提出了一个新的异步编程模型--基于事件的异步模型,即EAP(Event-based Asynchronous Pattern)。
实现了EAP的类具备一个或多个以Async为后缀的方法,以及对应的Completed事件,而且这些类支持异步方法的取消和进度报告。在.NET类库中,只有部分类实现了EAP,共17个。在这17个类中,开发过程当中使用最多的莫过于BackgroundWorker类了。
常用的属性为:
CancellationPending:用来指示应用程序是否已请求取消后台操做;
IsBusy:指示异步操做是否正在运行;
WorkReportProgress:只是BackgrounWorker可否报告进度;
WorkerSupportsCancellation:指示BackgroundWoker是否支持异步取消操做;
常用的方法为:
CancelAsync:请求取消异步操做;
ReportProgress:用于引起ProgressChanged事件;
RunWorkAsync:调用后开始执行异步操做;
常用到的3个事件为:
DoWork:调用RunWokerAsync时触发的事件;
ProgressChanged:调用ReportProgress时触发的事件,程序会在该事件中进行进度报告的更新;
RunWorkerCompleted:当异步操做已完成、被取消或引起异常时被触发。
这种方法已经不多用到了,因此这里就不详细介绍了。
四、TAP又是什么?
前面介绍了.NET提供的两种异步编程模式,分别为.NET 1.0中的APM和.NET 2.0中的EAP。虽然这两种异步编程模式能够实现多数状况下的异步编程,可是它们在MSDN文档上都被标注为了避免推荐使用的实现方式,由于在.NET 4.0中,微软又提供了更简单的异步编程实现方式--TAP,基于任务的异步模式。
该模式主要使用System.Threading.Tasks命名空间中的Task<T>类来实现异步编程,因此在采用TAP以前,首先要引入System.Threading.Tasks命名空间。
基于任务的异步模式(TAP,Task-based Asynchronous Pattern)只使用一个方法就能表示异步操做的开始和完成,而APM却须要Beginxxx和Endxxx两个方法分别表示开始和结束,EAP则要求具备以Async为后缀的方法和一个或多个事件。在基于任务的异步模式中,只须要一个以TaskAsync为后缀的方法,经过向该方法传入CancellationToken参数,咱们就能够很好地完成异步编程了。并且,还能够经过IProgress<T>接口来实现进度报告的功能。整体来讲,使用TAP会减小咱们的工做量,是代码更加简洁。
1 Task task=new Task(()=>{.......}); 2 task.Start();
五、让异步编程So easy——C# 5.0中的async和await
虽然.NET 1.0和.NET 2.0和.NET 4.0都对异步编程作了很好的支持,微软也逐渐地使用异步编程变得简单,但微软以为现有的工做还不够,它但愿使异步编程的开发过程更为简化,因此在.NET 4.5中,微软又提出了async和await两个关键字来支持异步编程。
这也是目前.NET Framework中最简单的异步编程实现方式,由于使用这个两个关键字进行异步编程,思考方式和实现同步编程时的彻底同样。
async和await关键字不会让调用方法运行在新线程中,而是将方法分割成多个片断(片断的界限出如今方法内部使用await关键字的位置处),并使其中一些片断能够异步运行。await关键字处的代码片断是在线程池线程上运行的,而整个方法的调用确实同步的。因此,使用此方式编程不用考虑跨线程访问UI控件的问题,从而大大下降了异步编程的出错率。