初识C#异步编程Task,await,async

来源:互联网 发布:乐乎网页版 编辑:程序博客网 时间:2024/05/21 19:42

今天看了msdn上关于Task,await和async的相关文档,对于其中的相关知识点做了摘抄,写下这篇作为总结。

Task

Task类表示一个异步操作。

Task 类的表示单个操作不返回一个值,通常以异步方式执行。 Task 对象是一个的中心思想 基于任务的异步模式 首次引入.NET Framework 4 中。 因为由执行工作 Task 对象通常以异步方式执行在线程池线程上而不是以同步方式在主应用程序线程,您可以使用 Status 属性,以及 IsCanceled, ,IsCompleted, ,和 IsFaulted 属性,以确定任务的状态。 大多数情况下,lambda 表达式用于指定的任务是执行的工作。

创建和执行任务
Task 可以多种方式创建实例。 最常用的方法,它位于开头 .NET Framework 4.5, ,是调用静态 Run 方法。 Run 方法提供了简单的方法来启动任务使用默认值,并且无需额外的参数。 下面的示例使用 Run(Action) 方法来启动循环,然后显示循环迭代数的任务︰

using System;using System.Threading.Tasks;public class Example{   public static void Main()   {      Task t = Task.Run( () => {                                  // Just loop.                                  int ctr = 0;                                  for (ctr = 0; ctr <= 1000000; ctr++)                                  {}                                  Console.WriteLine("Finished {0} loop iterations",                                                    ctr);                               } );      t.Wait();   }}// The example displays the following output://        Finished 1000001 loop iterations

替代方法,并启动任务的最常见方法 .NET Framework 4, ,是静态 TaskFactory.StartNew 方法。 Task.Factory 属性将返回 TaskFactory 对象。 重载 TaskFactory.StartNew 方法,你可以指定要传递给任务创建选项和任务计划程序参数。 下面的示例使用 TaskFactory.StartNew 方法来启动任务。 它是功能上等效于在前面的示例代码。

using System;using System.Threading.Tasks;public class Example{   public static void Main()   {      Task t = Task.Factory.StartNew( () => {                                  // Just loop.                                  int ctr = 0;                                  for (ctr = 0; ctr <= 1000000; ctr++)                                  {}                                  Console.WriteLine("Finished {0} loop iterations",                                                    ctr);                               } );      t.Wait();   }}// The example displays the following output://        Finished 1000001 loop iterations

等待一个或多个任务完成
因为任务通常以异步方式在线程池线程上运行,一旦该任务已实例化,创建并启动任务的线程将继续执行。 在某些情况下,当调用线程是主应用程序线程时,该应用程序可能会在其他任务开始执行之前终止。 有些情况下,应用程序的逻辑可能需要调用线程继续执行,仅当一个或多个任务执行完毕。 您可以同步调用线程的执行,以及启动异步任务并通过调用 Wait 方法来等待要完成的一个或多个任务。
若要等待完成一项任务,可以调用其 Task.Wait 方法。 调用 Wait 方法将一直阻塞调用线程直到单一类实例都已完成执行。
下面的示例调用无参数 Wait() 方法,以无条件地等待,直到任务完成。 该任务通过调用来模拟工作 Thread.Sleep 方法进入睡眠状态两秒钟。

using System;   using System.Threading;using System.Threading.Tasks;class Program{    static Random rand = new Random();    static void Main()    {        // Wait on a single task with no timeout specified.        Task taskA = Task.Run( () => Thread.Sleep(2000));        Console.WriteLine("taskA Status: {0}", taskA.Status);        try {          taskA.Wait();          Console.WriteLine("taskA Status: {0}", taskA.Status);       }        catch (AggregateException) {          Console.WriteLine("Exception in taskA.");       }       }    }// The example displays output like the following://     taskA Status: WaitingToRun//     taskA Status: RanToCompletion

您可以有条件地等待任务完成。 Wait(Int32) 和 Wait(TimeSpan) 方法阻止调用线程,直到任务完成或超时间隔结束,具体取决于第一个。 由于下面的示例将启动一个任务,它在睡眠两秒钟,但定义的一秒的超时值,调用线程受到阻止,直到超时到期和之前的任务已完成执行。

using System;using System.Threading;using System.Threading.Tasks;public class Example{   public static void Main()   {      // Wait on a single task with a timeout specified.      Task taskA = Task.Run( () => Thread.Sleep(2000));      try {        taskA.Wait(1000);       // Wait for 1 second.        bool completed = taskA.IsCompleted;        Console.WriteLine("Task A completed: {0}, Status: {1}",                         completed, taskA.Status);        if (! completed)           Console.WriteLine("Timed out before task A completed.");                        }       catch (AggregateException) {          Console.WriteLine("Exception in taskA.");       }      }}// The example displays output like the following://     Task A completed: False, Status: Running//     Timed out before task A completed.

你也可以通过调用提供一个取消标记 Wait(CancellationToken) 和 Wait(Int32, CancellationToken) 方法。 如果该令牌的 IsCancellationRequested 属性是 true, ,取消等待; 如果它变为 true 时 Wait 方法终止。
在某些情况下,您可能想要等待的执行的任务的一系列的第一个完成,但不关注它的任务。 出于此目的,您可以调用Task.WaitAll的重载方法之一 。 下面的示例创建三个任务,其中每个休眠的随机数字生成器确定时间间隔。 WaitAny(Task[]) 方法等待第一个任务完成。 此示例随后显示所有三个任务的状态的信息。

using System;using System.Threading;using System.Threading.Tasks;public class Example{   public static void Main()   {      var tasks = new Task[3];      var rnd = new Random();      for (int ctr = 0; ctr <= 2; ctr++)         tasks[ctr] = Task.Run( () => Thread.Sleep(rnd.Next(500, 3000)));      try {         int index = Task.WaitAny(tasks);         Console.WriteLine("Task #{0} completed first.\n", tasks[index].Id);         Console.WriteLine("Status of all tasks:");         foreach (var t in tasks)            Console.WriteLine("   Task #{0}: {1}", t.Id, t.Status);      }      catch (AggregateException) {         Console.WriteLine("An exception occurred.");      }   }}// The example displays output like the following://     Task #1 completed first.//     //     Status of all tasks://        Task #3: Running//        Task #1: RanToCompletion//        Task #4: Running

您也可以通过调用WaitAll 方法等待所有任务的调用完成 。 下面的示例创建十个任务,等待所有十若要完成,然后显示其状态。

public class Example{   public static void Main()   {      // Wait for all tasks to complete.      Task[] tasks = new Task[10];      for (int i = 0; i < 10; i++)      {          tasks[i] = Task.Run(() => Thread.Sleep(2000));      }      try {         Task.WaitAll(tasks);      }      catch (AggregateException ae) {         Console.WriteLine("One or more exceptions occurred: ");         foreach (var ex in ae.Flatten().InnerExceptions)            Console.WriteLine("   {0}", ex.Message);      }         Console.WriteLine("Status of completed tasks:");      foreach (var t in tasks)         Console.WriteLine("   Task #{0}: {1}", t.Id, t.Status);   }}// The example displays the following output://     Status of completed tasks://        Task #2: RanToCompletion//        Task #1: RanToCompletion//        Task #3: RanToCompletion//        Task #4: RanToCompletion//        Task #6: RanToCompletion//        Task #5: RanToCompletion//        Task #7: RanToCompletion//        Task #8: RanToCompletion//        Task #9: RanToCompletion//        Task #10: RanToCompletion
Task<TResult> 类表示一个可以返回值的异步操作。Task<TResult> 类的表示单个操作通常返回一个值,并以异步方式执行。 Task<TResult> 对象是一种.NET Framework 4 中引入的第一个主要的组件。 因为由执行工作 Task<TResult> 对象通常以异步方式执行在线程池线程上而不是以同步方式在主应用程序线程,您可以使用 Status 属性,以及 IsCanceled, ,IsCompleted, ,和 IsFaulted 属性,以确定任务的状态。 大多数情况下,lambda 表达式用于指定的任务是执行的工作。Task<TResult> 可以多种方式创建实例。 最常用的方法,它位于开头 .NET Framework 4.5, ,是调用静态 Task.Run<TResult>(Func<TResult>) 或 Task.Run<TResult>(Func<TResult>, CancellationToken) 方法。 这些方法提供简单的方法来启动任务,通过使用默认值,而无需获取其他参数。 下面的示例使用 Task.Run<TResult>(Func<TResult>) 方法来启动循环,然后显示循环迭代数的任务︰
using System;using System.Threading.Tasks;public class Example{   public static void Main()   {      var t = Task<int>.Run( () => {                                      // Just loop.                                      int max = 1000000;                                      int ctr = 0;                                      for (ctr = 0; ctr <= max; ctr++) {                                         if (ctr == max / 2 && DateTime.Now.Hour <= 12) {                                            ctr++;                                            break;                                         }                                      }                                      return ctr;                                    } );      Console.WriteLine("Finished {0:N0} iterations.", t.Result);   }}// The example displays output like the following://        Finished 1,000,001 loop iterations.

Console.WriteLine(“Finished {0:N0} iterations.”, t.Result),通过t.Result,主线程会挂起,等待task执行完成返回结果给主线程后,主线程才会继续往下执

替代方法,并启动任务的最常见方式 .NET Framework 4, ,是调用静态 TaskFactory.StartNew 或 TaskFactory<TResult>.StartNew 方法。 Task.Factory 属性将返回 TaskFactory 对象,与 Task<TResult>.Factory 属性将返回 TaskFactory<TResult> 对象。 重载其 StartNew 方法,可以将参数传递,定义任务创建选项,并指定任务计划程序。 下面的示例使用 TaskFactory<TResult>.StartNew(Func<TResult>) 方法来启动任务。 它是功能上等效于在前面的示例代码。
using System;using System.Threading.Tasks;public class Example{   public static void Main()   {      var t = Task<int>.Factory.StartNew( () => {                                      // Just loop.                                      int max = 1000000;                                      int ctr = 0;                                      for (ctr = 0; ctr <= max; ctr++) {                                         if (ctr == max / 2 && DateTime.Now.Hour <= 12) {                                            ctr++;                                            break;                                         }                                      }                                      return ctr;                               } );      Console.WriteLine("Finished {0:N0} iterations.", t.Result);   }}// The example displays the following output://        Finished 1000001 loop iterations

async

使用 async 修饰符可将方法、lambda 表达式或匿名方法指定为异步。

await

await 运算符在异步方法应用于任务,以挂起执行方法,直到所等待的任务完成。
在其中使用 await 的异步方法必须通过 async 关键字进行修改。 使用 async 修饰符定义并且通常包含一个或多个 await 表达式的这类方法称为异步方法。

在以下代码中,HttpClient 方法 GetByteArrayAsync 返回 Task<byte[]> (getContentsTask)。 当任务完成时,任务约定生成实际字节数组。 await 运算符应用于 getContentsTask 以在 SumPageSizesAsync 中挂起执行,直到 getContentsTask 完成。 同时,控制权会返回给 SumPageSizesAsync 的调用方。 当 getContentsTask 完成之后,await 表达式计算为字节数组。
private async Task SumPageSizesAsync()  {      // To use the HttpClient type in desktop apps, you must include a using directive and add a       // reference for the System.Net.Http namespace.      HttpClient client = new HttpClient();      // . . .      Task<byte[]> getContentsTask = client.GetByteArrayAsync(url);      byte[] urlContents = await getContentsTask;      // Equivalently, now that you see how it works, you can write the same thing in a single line.      //byte[] urlContents = await client.GetByteArrayAsync(url);      // . . .  }  
如前面的示例中所示,如果 await 应用于返回 Task<TResult> 的方法调用结果,则 await 表达式的类型为 TResult。 如果 await 应用于返回 Task 的方法调用结果,则 await 表达式的类型为 void。 以下示例演示了差异。
// Keyword await used with a method that returns a Task<TResult>.  TResult result = await AsyncMethodThatReturnsTaskTResult();  // Keyword await used with a method that returns a Task.  await AsyncMethodThatReturnsTask();  

await 表达式不阻止正在执行它的线程。 而是使编译器将剩下的异步方法注册为等待任务的延续任务。 控制权随后会返回给异步方法的调用方。 任务完成时,它会调用其延续任务,异步方法的执行会在暂停的位置处恢复。

await 表达式只能在由 async 修饰符标记的立即封闭方法体、lambda 表达式或异步方法中出现。 术语“await”在该上下文中仅用作关键字。 在其他位置,它会解释为标识符。 在方法、lambda 表达式或匿名方法中,await 表达式不能在同步函数体、查询表达式、lock 语句块或不安全上下文中出现。

下面的 Windows 窗体示例阐释如何在异步方法 WaitAsynchronouslyAsync 中使用 await。 将该方法的行为与 WaitSynchronously 的行为进行对比。 如果未向任务应用 await 运算符,WaitSynchronously 就会同步运行,而不管其定义中是否使用了 async 修饰符和在主体中是否调用了 Thread.Sleep。

private async void button1_Click(object sender, EventArgs e)  {      // Call the method that runs asynchronously.      string result = await WaitAsynchronouslyAsync();      // Call the method that runs synchronously.      //string result = await WaitSynchronously ();      // Display the result.      textBox1.Text += result;  }  // The following method runs asynchronously. The UI thread is not  // blocked during the delay. You can move or resize the Form1 window   // while Task.Delay is running.  public async Task<string> WaitAsynchronouslyAsync()  {      await Task.Delay(10000);      return "Finished";  }  // The following method runs synchronously, despite the use of async.  // You cannot move or resize the Form1 window while Thread.Sleep  // is running because the UI thread is blocked.  public async Task<string> WaitSynchronously()  {      // Add a using directive for System.Threading.      Thread.Sleep(10000);      return "Finished";  }  

最后,附上官网上贴出的异步方法的流程图示意图:

What Happens in an Async Method
The most important thing to understand in asynchronous programming is how the control flow moves from method to method. The following diagram leads you through the process.
这里写图片描述
The numbers in the diagram correspond to the following steps.
An event handler calls and awaits the AccessTheWebAsync async method.

  1. An event handler calls and awaits the AccessTheWebAsync async method.
  2. AccessTheWebAsync creates an HttpClient instance and calls the GetStringAsync asynchronous method to download the contents of a website as a string.
    GetStringAsync returns a Task where TResult is a string, and AccessTheWebAsync assigns the task to the getStringTask variable. The task represents the ongoing process for the call to GetStringAsync, with a commitment to produce an actual string value when the work is complete.
  3. Because getStringTask hasn’t been awaited yet, AccessTheWebAsync can continue with other work that doesn’t depend on the final result from GetStringAsync. That work is represented by a call to the synchronous method DoIndependentWork.
  4. DoIndependentWork is a synchronous method that does its work and returns to its caller.
  5. AccessTheWebAsync has run out of work that it can do without a result from getStringTask. AccessTheWebAsync next wants to calculate and return the length of the downloaded string, but the method can’t calculate that value until the method has the string

    Therefore, AccessTheWebAsync uses an await operator to suspend its progress and to yield control to the method that called AccessTheWebAsync. AccessTheWebAsync returns a Task to the caller. The task represents a promise to produce an integer result that’s the length of the downloaded string.

参考文档:

  • https://msdn.microsoft.com/zh-cn/library/system.threading.tasks.task(v=vs.110).aspx
  • https://msdn.microsoft.com/zh-cn/library/dd321424(v=vs.110).aspx
  • https://msdn.microsoft.com/zh-cn/library/hh156513.aspx
  • https://msdn.microsoft.com/zh-cn/library/hh156528.aspx
  • https://msdn.microsoft.com/zh-cn/library/mt674882.aspx
0 0
原创粉丝点击