C#异步模式
.net中提供了执行异步操作的三种模式
异步编程模型 (APM) 模式(也称为
基于事件的异步模式(EAP)
基于事件的异步模式 (EAP),是提供异步行为的基于事件的旧模型。 这种模式需要后缀为 Async
的方法,以及一个或多个事件、事件处理程序委托类型和 EventArg
派生类型。 EAP 是在 .NET Framework 2.0 中引入的。 现在微软不建议新开发中使用这种模式。
基于任务的异步模式(TAP)
基于任务的异步模式 (TAP),该模式使用单一方法表示异步操作的开始和完成。 TAP 是在 .NET Framework 4 中引入的。 这是在 .NET 中进行异步编程的推荐方法。
从.NET Framework1.0开始,.net中提供了异步属性,而且内置类当中许多类实现了一种或多种异步模式,例如发出HTTP请求并获取返回结果的同步和异步API则是一个例子。
同步调用
下面这个类为WebClient的同步版本。下面的代码片段中,DownloadString发出一个Http请求并将将响应内容赋值给了一个字符串,将该字符串写在控制台上,该代码在执行过程中会等待DownloadString方法执行完之后再执行下面的代码
private const string url = "http://www.cninnovation.com"; private static void SynchronizedAPI() { Console.WriteLine(nameof(SynchronizedAPI)); using (var client = new WebClient()) { string content = client.DownloadString(url); Console.WriteLine(content.Substring(0, 100)); } Console.WriteLine(); }
执行结果如下
方法DownloadString会阻塞调用进程,调用的应用程序会无响应直到返回想对应的结果。
对于WebClient类并没有对应的异步模式的实现,此处示例使用的是WebRequest类来代替,该类通过BeginGetRespond和EndGetRespond方法提供异步模式。在下面的代码片段中BeginGetResponse将异步HTTP GET请求发到服务器上,一旦网络请求完成则调用本地ReadResponse方法。
private static void AsynchronousPattern() { Console.WriteLine(nameof(AsynchronousPattern)); WebRequest request = WebRequest.Create(url); IAsyncResult result = request.BeginGetResponse(ReadResponse, null); void ReadResponse(IAsyncResult ar) { using (WebResponse response = request.EndGetResponse(ar)) { Stream stream = response.GetResponseStream(); var reader = new StreamReader(stream); string content = reader.ReadToEnd(); Console.WriteLine(content.Substring(0, 100)); Console.WriteLine(); } } }
基于事件的异步模式
对于同步方法DownloadString,WebClient类提供了一个异步变体方法DownloadStringAsync。以下代码演示基于事件的异步模式,当请求完成时会触发DownloadStringCompleted事件,该事件的第二个参数是DownloadStringCompleteEventArgs类型,这个类型通过Result
属性返回结果字符串。使用DownloadStringCompleted事件,事件处理程序将通过保存同步上下文的线程来调用,在windform等程序中这就是UI线程。
private static void EventBasedAsyncPattern() { Console.WriteLine(nameof(EventBasedAsyncPattern)); using (var client = new WebClient()) { client.DownloadStringCompleted += (sender, e) => { Console.WriteLine(e.Result.Substring(0, 100)); }; client.DownloadStringAsync(new Uri(url)); Console.WriteLine(); } }
基于事件的异步模式和同步编程之间的区别在于方法的调用的顺序,与同步方法的调用相比,顺序被颠倒了,调用异步方法之前需要定义这个方法完成时会发生什么。
基于任务的异步模式
在.NET Framework4.5及以后,对于同步方法DownloadString,Webclient类提供了一个异步变体方法DownloadStringTaskAsync,该方法声明为返回Task<string>,但是不需要声明Task<string>变量,只要声明string类型变量并使用await关键字。
await关键字会解除线程(这里是UI线程)的阻塞,完成其他任务,当DownloadStringTaskAsync方法完成后其后台处理后,UI线程则可以继续,从任务中获得结果,然后执行await关键字之后的代码。
private static async Task Main() { Console.WriteLine("任务开始"); TaskBasedAsyncPatternAsync(); //编译器会弹出建议”由于此调用不会等待,因此此调用完成之前将会继续执行当前。请考虑将await运算符用于调用结果“ Console.WriteLine("任务结束"); Console.ReadLine(); } private static async Task TaskBasedAsyncPatternAsync() { Console.WriteLine(nameof(TaskBasedAsyncPatternAsync)); using (var client = new WebClient()) { string content = await client.DownloadStringTaskAsync(url); await Task.Delay(3000); Console.WriteLine(content.Substring(0, 100)); Console.WriteLine("这是基于任务的异步编程"); } }
执行结果如下
基于任务的异步模式,编译器上会通过Task任务的形式去实现异步模式,await和async关键字是编译器功能,编译器内部仍然通过Task来实现对应的功能。从以上的执行结果我们可以看到
1.在async方法中遇到await关键字后,当前线程立即返回(到调用方),继续之前的处理逻辑;await关键字之后的代码逻辑,将交由新的线程处理;当新的线程处理完成后,可以从新的线程返回处理结果到调用(处)线程当中,结束等待。
2.在一个async方法中,会根据await关键字进行分割,拆分到不同的线程处理同一个方法的不同部分!
3.把一个方法代码的不同部分拆分到多个线程处理,这是异步方法和同步方法的最大不同!