async 和 await 是用来定义的异步方法,async 关键字是上下文关键字,原因在于只有当它修饰方法、lambda 表达式或匿名方法时,它才是关键字。 在所有其他上下文中,都会将其解释为标识符。
如果 async 关键字修改的方法不包含 await 表达式或语句,则该方法将同步执行。方法将同步运行,直至到达其第一个 await 表达式,此时会将方法挂起,直到等待的任务完成。 但编译器警告将通知你不包含 await 的任何异步方法,因为该情况可能表示存在错误。
异步方法的返回类型:
如果你的方法有操作数为 TResult 类型的返回语句,则为 Task<TResult>。 如果你的方法没有返回语句或具有没有操作数的返回语句,则为 Task,即,对方法的调用将返回一个 Task,但是当 Task 完成时,任何等待 Task 的所有 await 表达式的计算结果都为 void。
如果你编写的是异步事件处理程序,则为 Void(Visual Basic 中为 Sub), void 返回异步方法的调用方不能等待,并且无法捕获该方法引发的异常。
异步方法不能声明任何 ref 或 out 参数,但是可以调用具有这类参数的方法,在异步方法中,await 运算符应用于通过调用另一个异步方法返回的任务。方法通常包含至少一个 await 表达式,该表达式标记一个点,在该点上,直到等待的异步操作完成方法才能继续。 同时,将方法挂起,并且控件返回到方法的调用方
异步对可能起阻止作用的活动(例如,应用程序访问 Web 时)至关重要。 对 Web 资源的访问有时很慢或会延迟。 如果此类活动在同步过程中受阻,则整个应用程序必须等待。 在异步过程中,应用程序可继续执行不依赖 Web 资源的其他工作,直至潜在阻止任务完成。
一般情况下,异步方法的名称是: 在普通的方法名称后追加 “Async"
异步和等待
如果通过使用 Async 或 async 修饰符指定某种方法为异步方法,则可以启用以下两个功能。
1. 标记的异步方法可以使用 Await 或 await 来指定悬挂点。 await 运算符通知编译器异步方法只有直到等待的异步过程完成才能继续通过该点。 同时,控件返回至异步方法的调用方。
await 表达式中异步方法的挂起不能使该方法退出,并且 finally 块不会运行。
2. 标记的异步方法本身可以通过调用它的方法等待。
异步方法通常包含 await 运算符的一个或多个匹配项,但缺少 await 表达式不会导致编译器错误。 如果异步方法未使用 await 运算符标记悬挂点,则该方法将作为同步方法执行,不管异步修饰符如何。 编译器将为此类方法发布一个警告。
说到异步方法,不能不说说线程
异步方法旨在成为非阻止操作。 异步方法中的 await 表达式在等待的任务正在运行时不会阻止当前线程。 相反,表达式在继续时注册方法的其余部分并将控件返回到异步方法的调用方。
async 和 await 关键字不会导致创建其他线程。 因为异步方法不会在其自身线程上运行,因此它不需要多线程。 只有当方法处于活动状态时,该方法将在当前同步上下文中运行并使用线程上的时间。 可以使用 Task.Run 将占用大量 CPU 的工作移到后台线程,但是后台线程不会帮助正在等待结果的进程变为可用状态。
对于异步编程而言,该基于异步的方法优于几乎每个用例中的现有方法。 具体而言,此方法比 BackgroundWorker 更适用于 IO 绑定的操作,因为此代码更简单且无需防止争用条件。 结合 Task.Run 使用时,异步编程比 BackgroundWorker 更适用于 CPU 绑定的操作,因为异步编程将运行代码的协调细节与 Task.Run 传输至线程池的工作区分开来。
在异步方法中,可使用提供的关键字和类型来指示需要完成的操作,且编译器会完成其余操作,其中包括持续跟踪控件以挂起方法返回等待点时发生的情况。 一些常规流程(例如,循环和异常处理)在传统异步代码中处理起来可能很困难。 在异步方法中,元素的编写频率与同步解决方案相同且此问题得到解决。
由于所有与用户界面相关的活动通常共享一个线程,因此,异步对访问 UI 线程的应用程序来说尤为重要。如果任何进程在同步应用程序中受阻,则所有进程都将受阻。 你的应用程序停止响应,因此,你可能在其等待过程中认为它已经失败。
使用异步方法时,应用程序将继续响应 UI。 例如,你可以调整窗口的大小或最小化窗口;如果你不希望等待应用程序结束,则可以将其关闭。
当设计异步操作时,该基于异步的方法将自动传输的等效对象添加到可从中选择的选项列表中。 开发人员只需要投入较少的工作量即可使你获取传统异步编程的所有优点。
下面的示例演示了一种异步方法。 你应对代码中的几乎所有内容都非常熟悉。 注释调出你添加的用来创建异步的功能。
private async void StartButton_Click(object sender, RoutedEventArgs e) { // ExampleMethodAsync returns a Task<int>, which means that the method // eventually produces an int result. However, ExampleMethodAsync returns // the Task<int> value as soon as it reaches an await. ResultsTextBox.Text += "\n"; try { int length = await ExampleMethodAsync(); ResultsTextBox.Text += String.Format("Length: {0}\n", length); } catch (Exception) { // Process the exception if one occurs. } } public async Task<int> ExampleMethodAsync() { var httpClient = new HttpClient(); int exampleInt = (await httpClient.GetStringAsync("http://msdn.microsoft.com")).Length; ResultsTextBox.Text += "Preparing to finish ExampleMethodAsync.\n"; // After the following return statement, any method that‘s awaiting // ExampleMethodAsync (in this case, StartButton_Click) can get the // integer result. return exampleInt; } // Output: // Preparing to finish ExampleMethodAsync. // Length: 53292