zoukankan      html  css  js  c++  java
  • C#之异步编程

    1 异步编程的重要性

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

    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来代替

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

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

    2.3基于事件的异步模式

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

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

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

    2.4基于任务的异步模式

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

    现在代码简单多了,并且没有阻塞。

    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.
    "+"Result 1:{0}
     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.
    " + "Result 1:{0}
     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);

    现在用户就可以取消运行时间长的任务了。

  • 相关阅读:
    Linux ping 命令
    Linux ab 命令
    存储过程统计年月日的访问人数
    Struts Upload上传文件
    struts json登录
    javaScript没有块级作用域
    ThreadLocal(一):Thread 、ThreadLocal、ThreadLocalMap
    BeanUtils 学习教程
    WeakHashMap 理解笔记
    javaScript中的单引号与双引号
  • 原文地址:https://www.cnblogs.com/saodiseng2015/p/5058673.html
Copyright © 2011-2022 走看看