使用 Async 和 Await 的异步编程 #Reprinted#
string urlContents = await client.GetStringAsync();
以下特征总结了使上面一个异步方法。
-
方法签名包含一个 Async 或async 修饰符。
-
异步方法的名称以“Async”后缀,按照约定,关闭。
-
返回类型为下列类型之一:
-
Task<TResult>,如果您的方法具有操作个线程类型 TResult 的返回语句。
-
Task,如果方法没有返回语句或具有返回语句不操作。
-
无效 (在 Visual Basic 中子 ),如果您编写一个异步事件处理程序。
-
-
方法通常包含至少一个等待表达式,指示个方法无法继续,直到该等待的异步操作完成的。同时,方法被挂起,并且,控件返回到方法的调用方。本主题的下一节将解释发生的悬挂点。
在异步方法,可以使用提供的关键字和类型指示要执行,因此,编译器执行方式,包括记录必须出现,当控件处于挂起的方法时回时间点。某些实例处理,例如循环,而异常处理,可能很难进行在传统异步代码的句柄。在异步方法,解决您编写这些元素,因为在一个同步解决方案会并将问题。
任何异步方法
若要了解的最重要的操作在异步编程是控制流如何从方法移动到方法。下图通过处理导致生成。
在关系图的数值对应于以下步骤。
说明:
-
事件处理程序调用并等待 AccessTheWebAsync 异步方法。
-
AccessTheWebAsync 创建HttpClient 实例并调用GetStringAsync 异步方法下载网站内容作为字符串。
-
挂起团队进度的内容发生 GetStringAsync 发生。 可能必须等待网站下载或一些其他块的事件。若要避免妨碍资源,GetStringAsync 为控件对其调用方,AccessTheWebAsync。
GetStringAsync返回 TResult 是字符串的Task<TResult>,并且,AccessTheWebAsync 将任务指派给getStringTask 变量。在工作完成时,任务表示继续对定向到GetStringAsync,以提交导致实际字符串值。
-
由于 getStringTask 不等待,AccessTheWebAsync 可以继续执行不依赖于从 GetStringAsync的最终结果的其他工作。 该任务由为同步方法DoIndependentWork的调用表示。
-
DoIndependentWork 完成其工作并回调用方的同步方法。
-
AccessTheWebAsync 用完了它可以完成,不会受到getStringTask的结果的工作。接下来AccessTheWebAsync 若要计算并返回该下载的字符串的长度,但是,该方法无法计算该值,直到该方法具有字符串。
因此,AccessTheWebAsync 使用一个等待运算符挂起的进度并使控件到调用AccessTheWebAsync的方法。AccessTheWebAsync 返回Task(Of Integer) 或Task<int> 调用方。任务表示形式导致为下载的字符串的长度的整数结果。 (说明:如果GetStringAsync (并 getStringTask) 后,在AccessTheWebAsync 等待之前,控件在AccessTheWebAsync保持。成本挂起然后返回到AccessTheWebAsync 将浪费,如果异步调用过程 (getStringTask) 已经完成了,并且 AccessTheWebSync 不必等待最终结果。) 在调用方 (在此示例中的事件处理程序内),处理重复。调用方可能完成不依赖于从AccessTheWebAsync 的结果在等待该结果之前的其他工作,或调用方可能立即等待。在事件处理程序到达等待表达式时,应用程序集中精力GetStringAsync的完成。事件处理程序等待AccessTheWebAsync,并且,AccessTheWebAsync 等待GetStringAsync。
-
GetStringAsync完成并生成一个字符串结果。字符串结果不通过对GetStringAsync 的调用返回方式与您可能期望的。(请确保方法返回已在第 3 步 .) 的任务,字符串结果在表示方法的完成的任务,getStringTask存储。等待运算符从getStringTask检索结果。赋值语句将检索的结果赋给urlContents。
- GetStringAsync完成并生成一个字符串结果。字符串结果不通过对GetStringAsync 的调用返回方式与您可能期望的。(请确保方法返回已在第 3 步 .) 的任务,字符串结果在表示方法的完成的任务,getStringTask存储。等待运算符从getStringTask检索结果。赋值语句将检索的结果赋给urlContents。
如果您不熟悉异步编程,请需要一分钟考虑同步和异步行为之间的差异。一个同步方法返回,其工作完成 (第 5 步),但是,异步方法返回任务值,其工作挂起时 (第 3 步和第 6 步)。在异步方法最终完成其工作时,任务将会标记为已完成和结果,如果有,在任务中。
API 异步方法
在什么情况下可能想知道找到支持异步编程的方法 (如GetStringAsync。.NET Framework 4.5 包含与异步以及等待的许多成员。您可以通过附加到成员名称和Task 或Task<TResult>的返回类型“Async”后缀识别这些成员。例如,System.IO.Stream 选件类包含方法例如CopyToAsync、ReadAsync和WriteAsync 在同步方法CopyTo、Read和Write。
Windows 运行时 还包含您在 Windows 应用商店 apps 可以使用异步和等待的许多方法。
线程
异步方法旨在成为非阻塞操作。当等待的任务运行时,在异步方法的一个等待表达式不会阻止当前线程。相反,该表达式注册该方法的其余部分作为继续并返回控制对异步方法的调用方。
异步和等待关键字不会导致其他线程创建。因为异步方法本身并不会运行的线程,异步方法不需要多线程。只有 + 当方法处于活动状态,则方法在当前同步上下文中运行并使用在线程的时间。可以使用Task.Run 移动 CPU 工作移到后台线程,但是,后台线程不利于等待结果变得可用处理。
以异步编程的基于异步的方法优于于几乎每个用例的现有方法。具体而言,此方法比 IO 操作的BackgroundWorker 好,因为代码更为简单的,因此无需防止争用条件。与Task.Run的组合,异步编程的 CPU 操作的BackgroundWorker 好,因为异步编程从Task.Run 传输到线程池的工作分隔运行您的代码以协调详细信息。
异步和等待
如果指定使用 异步 或 异步 修饰符,方法是异步方法,可以实现以下两个函数。
-
清单异步方法可以使用 Await 或指定的 等待 悬挂点。等待运算符通知编译器异步方法不能继续点的过去,直到等待的异步过程完成。同时,控制权交还异步方法的调用方。
一个异步方法的备用在等待表达式的不构成从方法的退出,并且,finally 块不会运行。
-
清单异步方法本身可以通过调用它的方法等待。
异步方法通常包含等待运算符的一个或多个匹配项,但是,请假等待表达式不会导致编译器错误。如果异步方法不会将等待运算符指示悬挂点,方法尽管"修饰符执行,一个同步方法。编译器会发出此类方法的警告。
Async、async、Await和await 是上下文关键字。
返回类型和参数
在编程 .NET framework,异步方法通常返回Task 或Task<TResult>。在异步方法中,等待运算符应用于从调用返回到另一个异步方法的任务。
您指定 Task<TResult>,因为返回类型,则方法包含指定类型TResult操作上的一个返回 (Visual Basic) 或返回 (c#) 语句。
使用 Task,因为返回类型,则该方法没有返回语句或具有不返回操作线程的 return 语句。
下面的示例演示如何声明并调用返回Task<TResult> 或Task的方法。
// Signature specifies Task<TResult> 02.async Task<int> TaskOfTResult_MethodAsync() 03.{ 04. int hours; 05. // . . . 06. // Return statement specifies an integer result. 07. return hours; 08.} 09. 10.// Calls to TaskOfTResult_MethodAsync 11.Task<int> returnedTaskTResult = TaskOfTResult_MethodAsync(); 12.int intResult = await returnedTaskTResult; 13.// or, in a single statement 14.int intResult = await TaskOfTResult_MethodAsync(); 15. 16. 17.// Signature specifies Task 18.async Task Task_MethodAsync() 19.{ 20. // . . . 21. // The method has no return statement. 22.} 23. 24.// Calls to Task_MethodAsync 25.Task returnedTask = Task_MethodAsync(); 26.await returnedTask; 27.// or, in a single statement 28.await Task_MethodAsync();
每个返回的任务表示正在进行的工作。任务封装有关状态的信息异步过程和,最后,从进程的最终结果或处理引发的异常;如果未成功。
异步方法也是 Sub 方法 (Visual Basic) 或使 void 返回类型 (c# 中)。这将返回类型主要用于定义事件处理程序,void 返回类型需要。异步事件处理程序通常用作异步程序的起始点。
是 Sub 程序或具有 void 返回类型不能等待的异步方法和一个无效返回的方法的调用方无法捕获方法引发的任何异常。
异步方法不能声明在 Visual Basic 或ref 的ByRef 参数或在 C# 中http://msdn.microsoft.com/zh-cn/library/t3c3bfhx.aspx 参数,但是,方法可以调用具有这些参数的方法。
在 Windows 运行时 编程的异步 API 使之一返回类型,类似于任务:
-
IAsyncAction,对应于Task
命名约定
按照约定,您追加“Async”传递给具有Async 或async 修饰符方法的名称。
您可以忽略事件、基类或接口协定建议一个不同的名称约定。例如,您不应向常用事件处理程序重命名,例如Button1_Click。
相关主题
标题 |
描述 |
示例 |
---|---|---|
演示如何将同步 WPF 解决方法转换为异步 WPF 解决方案。 应用程序下载一系列网站。 |
||
添加 Task.WhenAll 到上一个演练中。使用WhenAll 启动同时下载。 |
||
演示如何同时启动几个任务。 |
||
描述异步方法可能会返回的类型并解释每个类型时正确。 |
||
跟踪控制流在连续详细在等待异步程序的表达式。 |
||
演示如何添加以下功能添加到的"解决方法: |
||
演示如何有效的异步操作重新启动的处理种情况下,在运行时。 |
||
WhenAny:.NET Framework 和 Windows 运行时之间的桥接(C# 和 Visual Basic) |
演示如何消除在任务之间 .NET framework 和 IAsyncOperations 在 Windows 运行时,以便可以使用WhenAny 与 Windows 运行时 方法。 |
|
演示如何消除在任务之间 .NET framework 和 IAsyncOperations 在 Windows 运行时,以便可以使用CancellationTokenSource 与 Windows 运行时 方法。 |
||
列出并演示如何使用异步的优点并等待访问文件。 |
||
演示在等待语句的控制流,并演示 单步执行、逐过程和跳出 命令的行为在异步方法中。 |
||
描述 .NET framework 中 asynchrony 的新架构。 该模式根据 Task 和Task<TResult> 类型。 |
||
在 Windows 应用商店 app 演示如何使用异步和等待。 |
||
提供异步编程的概述 Windows 运行时。 |
||
提供指向有关异步编程的各种视频。 |
完整的示例
下面的代码是从本主题讨论的 windows 演示基础 (WPF) 应用程序的 MainWindow.xaml.vb 或 MainWindow.xaml.cs 文件。可下载示例从Async 示例:从“异步编程的示例使用"和”等待
<span style="font-size: 14px;">using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; // Add a using directive and a reference for System.Net.Http; using System.Net.Http; namespace AsyncFirstExample { public partial class MainWindow : Window { // Mark the event handler with async so you can use await in it. private async void StartButton_Click(object sender, RoutedEventArgs e) { // Call and await separately. //Task<int> getLengthTask = AccessTheWebAsync(); //// You can do independent work here. //int contentLength = await getLengthTask; int contentLength = await AccessTheWebAsync(); resultsTextBox.Text += String.Format("\r\nLength of the downloaded string: {0}.\r\n", contentLength); } // Three things to note in the signature: // - The method has an async modifier. // - The return type is Task or Task<T>. (See "Return Types" section.) // Here, it is Task<int> because the return statement returns an integer. // - The method name ends in "Async." async Task<int> AccessTheWebAsync() { // You need to add a reference to System.Net.Http to declare client. HttpClient client = new HttpClient(); // GetStringAsync returns a Task<string>. That means that when you await the // task you'll get a string (urlContents). Task<string> getStringTask = client.GetStringAsync("http://msdn.microsoft.com"); // You can do work here that doesn't rely on the string from GetStringAsync. DoIndependentWork(); // The await operator suspends AccessTheWebAsync. // - AccessTheWebAsync can't continue until getStringTask is complete. // - Meanwhile, control returns to the caller of AccessTheWebAsync. // - Control resumes here when getStringTask is complete. // - The await operator then retrieves the string result from getStringTask. string urlContents = await getStringTask; // The return statement specifies an integer result. // Any methods that are awaiting AccessTheWebAsync retrieve the length value. return urlContents.Length; } void DoIndependentWork() { resultsTextBox.Text += "Working . . . . . . .\r\n"; } } } // Sample Output: // Working . . . . . . . // Length of the downloaded string: 41564.</span>
转载自:http://msdn.microsoft.com/zh-cn/library/hh191443.aspx (中文地址)
http://msdn.microsoft.com/en-us/library/vstudio/hh191443.aspx (英文地址)