目录
  • 一.延续任务
  • 二.同步上下文
  • 三.使用多个异步方法
    • 1.按顺序调用异步方法
    • 2.使用组合器
  • 四.转换异步模式
    • 五.错误处理
      • 1.异步方法的异常处理
      • 2.多个异步方法的异常处理
      • 3.使用AggregateException信息返回显示异常
    • 六.取消异步方法

      一.延续任务

      private async static void CallerWithAsync()
          {
            string result = await GreetingAsync("Stephanie");
            Console.WriteLine(result);
          }
      
          static Task<string> GreetingAsync(string name)
          {
            return Task.Run<string>(() =>
              {
                  Thread.Sleep(10000);
                  return name;
              });
          }
          
          GreetingAsync方法返回一个Task<string>对象。该Task<string>对象包含任务创建的信息,并保存到任务完成。Task类的ContinueWith方法定义了任务完成后就调用的代码。
              private static void CallerWithContinuationTask()
              {
      
                var t1 = GreetingAsync("Stephanie");
      
      
                t1.ContinueWith(t =>
                  {
                    string result = t.Result;
                    Console.WriteLine(result);
                  });
      
      
              }

      由于不使用await,线程不会在方法中等待,会执行完CallerWithContinuationTask()的代码。不会再ContinueWith这里等待,所以需要一个前台线程,不然会关闭所以线程。

      二.同步上下文

      CallerWithAsync和CallerWithContinuationTask方法在方法的不同阶段使用了不同的线程。

      static Task<string> GreetingAsync(string name)
              {
                return Task.Run<string>(() =>
                  {
                      Thread.Sleep(10000);
                    Console.WriteLine("running greetingasync in thread {0} and task {1}", Thread.CurrentThread.ManagedThreadId, Task.CurrentId);
                    return name;
                  });
              }
              private async static void CallerWithAsync()
              {
                Console.WriteLine("started CallerWithAsync in thread {0} and task {1}", Thread.CurrentThread.ManagedThreadId, Task.CurrentId);
                string result = await GreetingAsync("Stephanie");
                Console.WriteLine(result);
                Console.WriteLine("finished GreetingAsync in thread {0} and task {1}", Thread.CurrentThread.ManagedThreadId, Task.CurrentId);
              }
              
              private static void CallerWithContinuationTask()
              {
                Console.WriteLine("started CallerWithContinuationTask in thread {0} and task {1}", Thread.CurrentThread.ManagedThreadId, Task.CurrentId);
      
                var t1 = GreetingAsync("Stephanie");
      
      
                t1.ContinueWith(t =>
                  {
                    string result = t.Result;
                    Console.WriteLine(result);
                    Console.WriteLine("finished CallerWithContinuationTask in thread {0} and task {1}", Thread.CurrentThread.ManagedThreadId, Task.CurrentId);
                  });
              }

      使用async和await关键字,当await完成后,不需要进行任何处理,就能访问UI线程。默认情况下,生成的代码就会把线程转换到拥有同步上下文的线程中。调用异步方法的线程分配给了同步上下文,await完成之后将继续执行。
      如果不使用相同的同步上下文,必须调用Task类的ConfigureAwait(false).例如,一个WPF应用程序,其await后面的代码没有任何用到UI元素的代码。在这种情况下,避免切换到同步上下文会执行的更快。

      string s1 = await GreetingAsync("Stephanie").ConfigureAwait(false);

      三.使用多个异步方法

      在一个异步方法里,可以调用一个或多个异步方法。如何编写代码,取决于一个异步方法的结果是否取决于另一个异步方法。

      1.按顺序调用异步方法

      下面的例子,第第二个异步方法依赖于第一个异步方法的结果,await关键字就很有用。

        private async static void MultipleAsyncMethods()
        {
          string s1 = await GreetingAsync("Stephanie");
          string s2 = await GreetingAsync(s1);
          Console.WriteLine("Finished both methods.\n Result 1: {0}\n Result 2: {1}", s1, s2);
        }

      2.使用组合器

      如果第二个异步方法独立于第一个,每个异步方法可以都不使用await,而是把每个异步方法的返回结果赋值给Task变量,就会运行的更快。

        private async static void MultipleAsyncMethodsWithCombinators1()
        {
          Task<string> t1 = GreetingAsync("Stephanie");
          Task<string> t2 = GreetingAsync("Matthias");
          await Task.WhenAll(t1, t2);
          Console.WriteLine("Finished both methods.\n Result 1: {0}\n Result 2: {1}", t1.Result, t2.Result);
        }
      • Task.WhenAll组合器可以接受多个同一类型的参数,并返回同一类型的值。
      • Task.WhenAll是在所有传入方法的任务都完成了才返回Task。
      • WhenAny是在其中一个传入方法的任务完成了就返回。

      Task.WhenAll定义了几个重载版本。如果所有的任务返回相同的类型,那么该类型的数组可用于await返回的结果。

      string[] result = await Task.WhenAll(t1, t2);

      四.转换异步模式

      https://www.jb51.net/article/244023.htm讲了三种异步编程的模式。
      并非所有的.NET Framework类在.NET 4.5中都引入了新的异步方法。还有许多类只提供类BeginXXX和EndXXX方法的异步模式,可以使用TaskFactory类的FromAsync方法,它可以把使用异步模式的方法转换为基于任务的异步模式的方法。

      /创建一个委托,并引用同步方法Greeting
              private static Func<string, string> greetingInvoker = Greeting;
              
              
              static IAsyncResult BeginGreeting(string name, AsyncCallback callback, object state)
              {
                return greetingInvoker.BeginInvoke(name, callback, state);
              }
      
              static string EndGreeting(IAsyncResult ar)
              {
                return greetingInvoker.EndInvoke(ar);
              }
              
              //FromAsync方法的前两个参数是委托类型,传入BeginGreeting, EndGreeting的地址。后两个参数是输入的参数和对象状态参数。
              private static async void ConvertingAsyncPattern()
              {
                string r = await Task<string>.Factory.FromAsync<string>(BeginGreeting, EndGreeting, "Angela", null);
                Console.WriteLine(r);
              }

      五.错误处理

      如果调用异步方法没有等待,将异步方法放在try/catch中,就不捕获不到异常。

      private static void DontHandle()
              {
                try
                {
                  ThrowAfter(200, "first");
                  // exception is not caught because this method is finished before the exception is thrown
                }
                catch (Exception ex)
                {
                  Console.WriteLine(ex.Message);
                }
              }
              
              static async Task ThrowAfter(int ms, string message)
              {
                  Console.Write("xxx");
                await Task.Delay(ms);
                throw new Exception(message);
              }

      DontHandle方法调用ThrowAfter后,不会在该处等待,会继续执行,不再保持对ThrowAfter方法的引用。
      注意:返回void的异步方法永远不会等待.异步方法最好返回一个Task类型。

      1.异步方法的异常处理

      使用await关键字,将其放在在try/catch中

      private static async void HandleOneError()
              {
                try
                {
                  await ThrowAfter(2000, "first");
                }
                catch (Exception ex)
                {
                  Console.WriteLine("handled {0}", ex.Message);
                }
              }

      2.多个异步方法的异常处理

      如果按照下面的代码,第二个异常将不会抛出。因为第一个异常已经抛出,直接调到catch块内了。

      private static async void StartTwoTasks()
              {
                try
                {
                  await ThrowAfter(2000, "first");
                  await ThrowAfter(1000, "second"); // the second call is not invoked because the first method throws an exception
                }
                catch (Exception ex)
                {
                  Console.WriteLine("handled {0}", ex.Message);
                }
              }

      使用Task.WhenAll,不管任务是否抛出异常,都会等到两个任务完成。所以会抛出两个异常。
      但是,只能看见传递给Task.WhenAll方法的第一个任务的异常信息,虽然第二个异常会抛出,但不会显示:

      private async static void StartTwoTasksParallel()
                  {
                    Task t1 = null;
                    Task t2 = null;
                    try
                    {
                      t1 = ThrowAfter(2000, "first");
                       t2 = ThrowAfter(1000, "second");
                      await Task.WhenAll(t1, t2);
                    }
                    catch (Exception ex)
                    {
                      // just display the exception information of the first task that is awaited within WhenAll
                      Console.WriteLine("handled {0}", ex.Message);
                    }
                  }

      3.使用AggregateException信息返回显示异常

      将Task.WhenAll返回的结果写到一个Task变量中,catch语句只检索到第一个任务的异常,但可以访问外部任务taskResult的Exception属性。Exception属性是AggregateException类型。这个异常类型定义了InnerExceptions属性,它包含了等待中的所有异常的列表。

      private static async void ShowAggregatedException()
              {
                Task taskResult = null;
                try
                {
                  Task t1 = ThrowAfter(2000, "first");
                  Task t2 = ThrowAfter(1000, "second");
                  await (taskResult = Task.WhenAll(t1, t2));
                }
                catch (Exception ex)
                {
                  // just display the exception information of the first task that is awaited within WhenAll
                  Console.WriteLine("handled {0}", ex.Message);
                  foreach (var ex1 in taskResult.Exception.InnerExceptions)
                  {
                    Console.WriteLine("inner exception {0} from task {1}", ex1.Message, ex1.Source);
                  }
                }
              }

      六.取消异步方法

      如果后台任务可能运行很长时间,就可能用的取消任务。
      取消框架基于协助行为,不是强制性的。一个运行时间很长的任务需要检查自己是否被取消,在这种情况下,它的工作就是清理所有已打开的资源,并结束相关工作。
      取消基于CancellationTokenSource类,该类可用于发送取消请求。请求发送给引用CancellationToken类的任务,其中CancellationToken类和CancellationTokenSource相关联。

      private CancellationTokenSource cts = new CancellationTokenSource();
              
              //添加一个按钮,用于取消正在运行的任务。使用cts.Cancel();
               private void button5_Click(object sender, EventArgs e)
               {
                   if (cts != null)
                       cts.Cancel();
               }
               
              private async void button4_Click(object sender, EventArgs e)
              {
                  
                  string s =  await AsyncTaskTest();
              }
      
              //向Task类的Run方法传递CancellationToken参数。但需要检查是否请求了取消操作。
               Task<string> AsyncTaskTest()
              {
                  return Task.Run(() =>
                      {
                      cts.Token.ThrowIfCancellationRequested();
                          Thread.Sleep(5000);
                          return "异步完成";
                      }
                  , cts.Token);
              }

      以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。

      声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。