C#之异步编程

1 异步编程的重要性

  C#5.0最重要的改进是提供了更强大的异步编程,C#5.0仅增长两个关键字Async和Await,使用异步编程,方法调用是后台运行(一般在线程和任务的帮助下),而且不会阻塞调用线程。html

2 异步模式

  从.net1.0开始就提供了异步特性,并且.NET Framework的许多类都实现了一个或多个异步模式(委托也实现了异步模式)。由于在WIndows From和WPF中用异步模式更新界面比较复杂,因此在2.0中提供了基于事件的异步模式。如今在4.5中推出了另一种新的方式来实现异步编程:基于任务的异步模式。这种模式是基于4.0中新增的Task类型,和一个利用Async和Await关键字的编译器功能。编程

2.1 同步调用

 1 WebClient wc = new WebClient(); 2 string s= wc.DownloadString("你的URL"); 框架

  运行该代码,程序一直在等待DownloadString的完成,在这种状况下使用异步很是有必要的。异步

2.2异步模式

使用异步模式是进行异步调用的方式之一,实现异步模式定义BeginXXX和EndXXX方法。若是有一个同步方法DownloadStrring,异步方法将转化成了两个方法BeginDownloadString和EndDownloadString方法。BeginXXX方法接受同步方法的全部输入的参数,而且还定义了一个AsyncCallBack参数,用于接收在异步方法执行完毕后的被调用的委托,该方法返回一个IAsyncResult,用于验证调用是否已经完成,而且一直等待,直到方法的执行完毕。EndXXX方法同步方法的全部的输出参数,并按照同步方法的返回类型来返回结果。WebClient类没有实现异步模式,但能够用HttpWebRequest来代替async

1  HttpWebRequest req =(HttpWebRequest)WebRequest.Create("你的URL"); 2             IAsyncResult result=  req.BeginGetResponse("",); 3             req.EndGetResponse();

异步模式的优点是使用委托功能就能实现异步编程,不用改变程序的行为,也不会阻塞界面的操做。异步编程

2.3基于事件的异步模式

  基于事件的异步模式定义了一个带有"Aysnc"后缀的方法。异步方法完成时不是定义被调用的委托,而是定义事件。post

1 WebClient client = new WebClient(); 2             client.DownloadStringCompleted += (sender, e1) => { }; 3             client.DownloadStringAsync(new Uri("你的URL"));

基于事件的异步方式的优点是易用。spa

2.4基于任务的异步模式

1 public async void Fun() 2  { 3             WebClient client = new WebClient(); 4             string s = await client.DownloadStringTaskAsync("你的URL"); 5         }

如今代码简单多了,而且没有阻塞。.net

3 异步编程的基础

  async和await关键字只是编译器功能,编译器会用Task建立代码。线程

3.1建立任务

1         static string Greeting(string name) 2  { 3             Thread.Sleep(2000); 4             return string.Format("Hello ,{0}", name); 5  } 6         static Task<string> GreetingAsync() 7  { 8             return Task.Run<string>(()=> { return Greeting(name); }); 9         }

 

3.2调用异步方法

  可使用Await关键字来调用返回任务的异步方法GreetingAsync,使用Await关键字须要使用Async修饰声明的方法。在完成GreetingAsync方法前该方法内的其它代码不会被执行。

1   private async static void CallWithAsync() 2  { 3             string result = await GreetingAsync("郑隆"); 4  Console.WriteLine(result); 5         }

若是异步方法的结果不传递给变量,也能够直接在参数中使用Await关键字。

1  private async static void CallWithAsync2() 2  { 3             Console.WriteLine(await GreetingAsync("郑隆")); 4         }
Async修饰符只能用于返回Task和void的方法。不能用于程序的入口。

3.3延续任务

Task类的ContinueWith方法定义了任务完成后将要调用的代码。

1    private static void CallerWithContinuationTask() 2  { 3             Task<string> t1 = GreetingAsync("郑隆"); 4             t1.ContinueWith(t => { string result = t.Result; Console.WriteLine(result); }); 5         }

 

3.4使用多个异步方法

在一个异步方法中,能够调用不止一个异步方法。

1  public async static void MultipleAsyncMethods() 2  { 3             string s1 = await GreetingAsync("zhenglong1"); 4             string s2 = await GreetingAsync("zhenglong2"); 5             Console.WriteLine("Finished both methods.\n"+"Result 1:{0}\n Result 2:{1}",s1,s2); 6         }

Task.WhenAll组合器,可让你等待直到两个任务都完成。

1  public async static void MultipleAsyncMethodsWithCombinators1() 2  { 3             Task<string>  t1= GreetingAsync("zhenglong1"); 4             Task<string> t2 = GreetingAsync("zhenglong2"); 5             await Task.WhenAll(t1,t2); 6             Console.WriteLine("Finished both methods.\n" + "Result 1:{0}\n Result 2:{1}", t1.Result, t2.Result); 7         }

Task的WhenAll方法是在全部传入的任务都完成了才返回Task,而WhenAny是在其中的一个任务已完成就会返回Task。

3.5转换异步模式

并不是.NET Freamwork的全部的类在4.5中都引入了新的异步方法。

 1 private static Func<string, string> greetingInvoker = Greeting;  2 
 3         static IAsyncResult BeginGreeting(string name,AsyncCallback callback,object state)  4  {  5             return greetingInvoker.BeginInvoke(name, callback, state);  6  }  7         static string EndGreeting(IAsyncResult ar)  8  {  9            return greetingInvoker.EndInvoke(ar); 10  } 11  private async static void ConvertingAsyncPattern() 12  { 13             string s = await Task<string>.Factory.FromAsync(BeginGreeting, EndGreeting, "zhenglong", null); 14  Console.WriteLine(s); 15         }

4 错误处理

 1   public static async Task ThrowAfter(int ms,string message)  2  {  3             await Task.Delay(ms);  4             throw new Exception(message);  5  }  6 
 7         public static void DontHandle()  8  {  9             try
10  { 11                 ThrowAfter(200,"first"); 12  } 13             catch (Exception ex) 14  { 15  Console.WriteLine(ex.Message);17  } 18         }

以上代码并不能捕获到异常,应为在ThrowAfter抛出异常以前DontHandle已经执行完了。

4.1 异步方法的异常处理

异步方法异常的一个较好的处理是使用关键字Await,而后将其放在try中

 1    public async static void HandleErrorOne()  2  {  3             try
 4  {  5                 await ThrowAfter(200, "first");  6  }  7             catch (Exception ex)  8  {  9  Console.WriteLine(ex.Message); 10  } 11         }

 

4.2 多个异步方法的异常处理

 1   public static async void StatTwoTasks()  2  {  3             try
 4  {  5                 await ThrowAfter(2000,"frist");  6                 await ThrowAfter(1000, "second");  7  }  8             catch (Exception ex)  9  { 10  Console.WriteLine(ex.Message); 11  } 12         }

如上代码并不能捕获所有的异常,缘由是由于在第一个异常抛出后程序就进入了catch。解决方法

 1  public static async void StatTwoTasksParallel()  2  {  3             try
 4  {  5                 Task t1 = ThrowAfter(200, "zhenglong1");  6                 Task t2 = ThrowAfter(200, "zhenglong2");  7                 await Task.WhenAll(t1,t2);  8  }  9             catch (Exception ex) 10  { 11  Console.WriteLine(ex.Message); 12  } 13         }

 

4.3 AggregateException类

为了获得全部的异常信息,能够将Task.WhenAll返回的结果写到一个Task中,这个任务一直等待全部的任务完成。

 1 public static async void ShowAggregateException()  2  {  3             Task taskResult = null;  4             try
 5  {  6                 Task t1 = ThrowAfter(200, "zhenglong1");  7                 Task t2 = ThrowAfter(200, "zhenglong2");  8                 await(taskResult= Task.WhenAll(t1, t2));  9  } 10             catch (Exception ex) 11  { 12  Console.WriteLine(ex.Message); 13                 foreach (var item in taskResult.Exception.InnerExceptions) 14  { 15  Console.WriteLine(item.Message); 16  } 17  } 18         }

5取消

在某种状况下,后台任务可能运行很长的时间,取消任务就很是有用了。

5.1 开始取消任务

1 private CancellationTokenSource cts; 2         public void Cancle() 3  { 4             if (cts != null) 5  cts.Cancel(); 6         }
CancellationTokenSource 类还支持在指定时间后才取消任务,CancelAfter方法在应该取消的任务后传入一个时间值,单位是ms.

5.2开始框架的特性任务取消

1  public async void CancelTwo() 2  { 3             cts = new CancellationTokenSource(); 4             HttpClient hc = new HttpClient(); 5             var s=await hc.GetAsync("Url", cts.Token); 6         }

5.3取消自定义任务

1  await Task.Run(()=> { },cts.Token);

如今用户就能够取消运行时间长的任务了。

转载于:https://www.cnblogs.com/saodiseng2015/p/5058673.html