文章内容

2017/11/5 17:18:01,作 者: 黄兵

await(C# 参考)

await 运算符应用于异步方法中的任务,在方法的执行中插入挂起点,直到所等待的任务完成。 任务表示正在进行的工作。

await 仅可用于由 async 关键字修改的异步方法中。 使用 async 修饰符定义并且通常包含一个或多个 await 表达式的这类方法称为异步方法。

备注

async 和 await 关键字是在 C# 5 中引入的。 有关异步编程的说明,请参阅使用 Async 和 Await 的异步编程

应用 await 运算符的任务通常由实现基于任务的异步模式的方法调用返回。 包括返回 TaskTask<TResult> 和 System.Threading.Tasks.ValueType<TResult> 对象的方法。

如下示例中,HttpClient.GetByteArrayAsync 方法返回 Task<byte[]>。 当任务完成时,任务约定生成实际字节数组。 await 运算符挂起执行,直到 GetByteArrayAsync 方法完成其操作。 同时,控制权会返回给 GetPageSizeAsync 的调用方。 任务结束执行时,await 表达式的计算结果为字节数组。

C#
using System;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;

public class Example
{
   public static void Main()
   {
      string[] args = Environment.GetCommandLineArgs();
      if (args.Length > 1)
         GetPageSizeAsync(args[1]).Wait();
      else
         Console.WriteLine("Enter at least one URL on the command line.");
   }

   private static async Task GetPageSizeAsync(string url)  
   {  
       var client = new HttpClient();  
       var uri = new Uri(Uri.EscapeUriString(url));
       byte[] urlContents = await client.GetByteArrayAsync(uri);
       Console.WriteLine($"{url}: {urlContents.Length/2:N0} characters");  
   }  
}
// The following call from the command line:
//    await1 http://docs.microsoft.com
// displays output like the following: 
//   http://docs.microsoft.com: 7,967 characters


重要

有关完整示例,请参阅演练:使用 async 和 await 访问 Web。 可以从 Microsoft 网站的开发者代码示例中下载示例。 该示例处于 AsyncWalkthrough_HttpClient 项目中。

如前一示例所示,如果 await 应用于返回 Task<TResult> 的方法调用的结果,则 await 表达式的类型为 TResult。 如果 await 应用于返回 Task 的方法调用的结果,则 await 表达式的类型为 void。 以下示例演示了差异。

C#
// await keyword used with a method that returns a Task<TResult>.  
TResult result = await AsyncMethodThatReturnsTaskTResult();  

// await keyword used with a method that returns a Task.  
await AsyncMethodThatReturnsTask();  

// await keyword used with a method that returns a ValueTask<TResult>.
TResult result = await AsyncMethodThatReturnsValueTaskTResult();

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

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

异常

大多数异步方法返回 Task 或 Task<TResult>。 返回任务的属性携带有关其状态和历史记录的信息,如任务是否完成、异步方法是否导致异常或已取消以及最终结果是什么。 await 运算符通过在由 GetAwaiter 方法返回的对象上调用方法来访问那些属性。

如果等待的返回任务的异步方法会导致异常,则 await 运算符重新引发异常。

如果等待的任务返回异步方法取消,则 await 运算符会重新引发 OperationCanceledException

处于故障状态的单个任务可以反映多个异常。 例如,任务可能是对 Task.WhenAll 调用的结果。 等待此类任务时,等待操作仅重新引发异常之一。 但是,无法预测重新引发的异常。

有关异步方法中的错误处理的示例,请参阅 try catch

示例

下面的示例返回页面(页面 URL 作为命令行参数传递给页面)中的字符总数。 此示例调用 GetPageLengthsAsync 方法,此方法标记有 async 关键字。 而 GetPageLengthsAsync 方法使用 await 关键字等待对 HttpClient.GetStringAsync 方法的调用。

C#
using System;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;

class Example
{
   static void Main()
   {
      string[] args = Environment.GetCommandLineArgs();
      if (args.Length < 2)
         throw new ArgumentNullException("No URLs specified on the command line.");
      
      long characters = GetPageLengthsAsync(args).Result;
      Console.WriteLine($"{args.Length - 1} pages, {characters:N0} characters");
   }

   private static async Task<long> GetPageLengthsAsync(string[] args)
   {
      var client = new HttpClient();
      long pageLengths = 0;

      for (int ctr = 1; ctr < args.Length; ctr++) {
         var uri = new Uri(Uri.EscapeUriString(args[ctr]));
         string pageContents = await client.GetStringAsync(uri);
         Interlocked.Add(ref pageLengths, pageContents.Length);
      }
      return pageLengths;
   }
}

由于不支持在应用程序入口点中使用 async 和await,因此我们无法将 async 属性应用到 Main 方法,也无法等待 GetPageLengthsAsync 方法调用。 我们可通过检索 Task<TResult>.Result 属性的值来确保 Main 方法等待异步操作完成。 对于不返回值的任务,可调用 Task.Wait 方法。

本文转载自:await(C# 参考)

分享到:

发表评论

评论列表