Await
https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/await https://learn.microsoft.com/en-us/dotnet/csharp/asynchronous-programming/ https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/expressions#1299-await-expressions
概念¶
- await 的作用是,将其修饰的表达式后面的流程作为回调加给 task;
- 有的描述中会说 “方法会从 await ‘暂停’ 的地方继续执行”,这实际上是因为
await后的代码作为回调被执行了,与 “暂停” 这个概念没有关系; - “暂停” 这个概念的产生或许是因为调试的时候,看起来
await前后还是在同一个方法里,但前后的流程可能并没有在相同线程中执行(当然也有可能在相同线程中,取决于任务调度); - 如果直接调用一个
Task而不await,那么它会在线程池线程上执行,但是无法正常捕获异常; - 当使用
await被应用到 task 上时,失败的 task 会抛出异常; - 如果下一步的开始依赖于上一步的结果,那么异步甚至比同步更慢,因为还额外有线程调度的时间;
- 如果几个任务之间是独立的,那么异步执行能够显著提高效率;
- 通过分离
Task的执行和await来实现独立任务同时在不同线程上的执行; - Task.Exception 属性是 System.AggregateException 类型的对象,因为异步任务执行的过程中可能会抛出多个异常;
- 如果 Task.Exception 是空的,那么会创建一个 AggregateException 异常,并抛出这集合中的第一个异常;
async/await在语义上与ContinueWith类似,但实际上编译器并没有将await表达式直接转换成ContinueWith。编译器生成优化过的状态机代码,提供类似的运行逻辑。await的方法并不是一定要有线程切换,await只是标记了一个同步上下文切换点;
几个例子¶
public class AwaitOperator
{
public static async Task Main()
{
Task<int> downloading = DownloadDocsMainPageAsync();
Console.WriteLine($"{nameof(Main)}: Launched downloading.");
int bytesLoaded = await downloading;
Console.WriteLine($"{nameof(Main)}: Downloaded {bytesLoaded} bytes.");
}
private static async Task<int> DownloadDocsMainPageAsync()
{
Console.WriteLine($"{nameof(DownloadDocsMainPageAsync)}: About to start downloading.");
var client = new HttpClient();
byte[] content = await client.GetByteArrayAsync("https://learn.microsoft.com/en-us/");
Console.WriteLine($"{nameof(DownloadDocsMainPageAsync)}: Finished downloading.");
return content.Length;
}
}
// Output similar to:
// DownloadDocsMainPageAsync: About to start downloading.
// Main: Launched downloading.
// DownloadDocsMainPageAsync: Finished downloading.
// Main: Downloaded 27700 bytes.
- Task 完成后会通知(实际是以回调的形式执行 await 后的操作)
Coffee cup = PourCoffee();
Console.WriteLine("Coffee is ready");
Task<Egg> eggsTask = FryEggsAsync(2);
Task<HashBrown> hashBrownTask = FryHashBrownsAsync(3);
Task<Toast> toastTask = ToastBreadAsync(2);
Toast toast = await toastTask;
ApplyButter(toast);
ApplyJam(toast);
Console.WriteLine("Toast is ready");
Juice oj = PourOJ();
Console.WriteLine("Oj is ready");
Egg eggs = await eggsTask;
Console.WriteLine("Eggs are ready");
HashBrown hashBrown = await hashBrownTask;
Console.WriteLine("Hash browns are ready");
Console.WriteLine("Breakfast is ready!");