await 运算符 - 异步等待任务完成
await
运算符暂停对其所属的 async 方法的求值,直到其操作数表示的异步操作完成。 异步操作完成后,await
运算符将返回操作的结果(如果有)。 当 await
运算符应用到表示已完成操作的操作数时,它将立即返回操作的结果,而不会暂停其所属的方法。 await
运算符不会阻止计算异步方法的线程。 当 await
运算符暂停其所属的异步方法时,控件将返回到方法的调用方。
在下面的示例中,HttpClient.GetByteArrayAsync 方法返回 Task<byte[]>
实例,该实例表示在完成时生成字节数组的异步操作。 在操作完成之前,await
运算符将暂停 DownloadDocsMainPageAsync
方法。 当 DownloadDocsMainPageAsync
暂停时,控件将返回到 Main
方法,该方法是 DownloadDocsMainPageAsync
的调用方。 Main
方法将执行,直至它需要 DownloadDocsMainPageAsync
方法执行的异步操作的结果。 当 GetByteArrayAsync 获取所有字节时,将计算 DownloadDocsMainPageAsync
方法的其余部分。 之后,将计算 Main
方法的其余部分。
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.
必须提供 await
表达式的操作数,才能在任务完成时进行通知。 通常,任务完成时(无论成功还是失败)都会调用委托。 C# 语言规范的 await
部分提供了有关如何实现这些通知的详细信息。
前一个示例使用异步 Main
方法。 有关详细信息,请参阅 Main 方法中的 await 运算符部分。
注意
有关异步编程的介绍,请参阅使用 async 和 await 的异步编程。 利用 async
和 await
的异步编程遵循基于任务的异步模式。
只能在通过 async 关键字修改的方法、lambda 表达式或匿名方法中使用 await
运算符。 在异步方法中,不能在同步函数的本地主体、lock 语句块内以及不安全的上下文中使用 await
运算符。
await
运算符的操作数通常是以下其中一个 .NET 类型:Task、Task<TResult>、ValueTask 或 ValueTask<TResult>。 但是,任何可等待表达式都可以是 await
运算符的操作数。 有关详细信息,请参阅 C# 语言规范中的可等待表达式部分。
如果表达式 t
的类型为 Task<TResult> 或 ValueTask<TResult>,则表达式 await t
的类型为 TResult
。 如果 t
的类型为 Task 或 ValueTask,则 await t
的类型为 void
。 在这两种情况下,如果 t
引发异常,则 await t
将重新引发异常。
异步流和可释放对象
可使用 await foreach
语句来使用异步数据流。 有关详细信息,请参阅迭代语句一文的 foreach
语句部分。
可使用 await using
语句来处理异步可释放对象,即其类型可实现 IAsyncDisposable 接口的对象。 有关详细信息,请参阅实现 DisposeAsync 方法一文中的使用异步可释放对象部分。
Main 方法中的 await 运算符
作为应用程序入口点的 Main
方法可以返回 Task
或 Task<int>
,使其成为异步的,以便在其主体中使用 await
运算符。 在较早的 C# 版本中,为了确保 Main
方法等待异步操作完成,可以检索由相应的异步方法返回的 Task<TResult> 实例的 Task<TResult>.Result 属性值。 对于不生成值的异步操作,可以调用 Task.Wait 方法。 有关如何选择语言版本的信息,请参阅 C# 语言版本管理。
C# 语言规范
有关详细信息,请参阅 C# 语言规范中的 Await 表达式部分。