使用 Async 和 Await 的异步编程(C# 和 Visual Basic)
使用异步编程,可以避免性能瓶颈和增强应用程序的总体响应能力。但是,编写异步应用程序的以前的技术可能比较复杂,使它们难以编写,调试和维护。
Visual Studio 2012 引入了一个简化的方法,编程",在 .NET Framework 4.5 和 Windows 运行时利用异步支持。编译器实现用于的开发人员执行和应用程序保留一个逻辑结构类似于同步代码的难题工作。因此,您获取异步编程的所有优点使用一小部分工作。
本主题包括下列各节。
- "提高响应能力
- 异步方法容易编写
- 任何异步方法。
- API 异步方法
- 线程
- 异步和等待
- 返回类型和参数
- 命名约定
- 相关主题
- 完整的示例
- 相关主题
本主题何时以及如何概述使用编程的异步并包含链接支持包含详细信息和示例的主题。
"提高响应能力
Asynchrony 到可能会阻塞事件很重要,例如,当您的应用程序访问该 web。对 web 资源的访问有时很慢或延迟。如果该事件在块同步过程,整个应用程序必须等待。在异步过程,应用程序可以继续不依赖于 web 资源的其他工作,直到潜在块任务完成。
下表显示了典型的区域异步编程的位置提高响应能力。从 .NET Framework 4.5 的列表的 API 和 Windows 运行时 包含支持异步编程的方法。
应用程序范围 |
支持包含异步方法的 API |
---|---|
Web 访问 |
|
处理文件 |
|
使用图像处理 |
|
WCF 编程 |
|
与套接字处理 |
因为所有与用户界面相关的事件通常共享一个线程,Asynchrony 证明尤为重要应用程序的访问 UI 线程。如果任何在一个同步应用程序,所有处理块阻塞。应用程序停止响应,因此,您可能认为,它失败,它在等待。
使用异步方法时,应用程序将继续响应 UI。可以调整或最小化窗口,例如也可以关闭应用程序,如果您不希望等待其结束。
该基于异步的方法添加自动传输的等效到可从中选择,在设计异步操作期间选项的列表。即获取旧异步编程的所有优点,但从开发人员的少量工作即可。
异步方法容易编写
在 Visual Basic 中 异步 和 Await 关键字和 c# 中的 异步 和 等待 关键字是异步编程的焦点。使用这两个关键字,则在 .NET framework 或 Windows 运行时 可以使用资源几乎一样轻松地创建一个异步方法,也可以创建一个同步方法。您定义使用异步并等待的异步方法引用异步方法。
下面的示例演示一个异步方法。几乎所有内容。代码应查找完全熟悉您。注释调用您添加创建 asynchrony 的功能。
可以找到完整示例文件本主题末尾的,因此,您可以下载示例从 Async 示例:从“异步编程的示例使用"和”等待。
' Three things to note in the signature:
' - The method has an Async modifier.
' - The return type is Task or Task(Of T). (See "Return Types" section.)
' Here, it is Task(Of Integer) because the return statement returns an integer.
' - The method name ends in "Async."
Async Function AccessTheWebAsync() As Task(Of Integer)
' You need to add a reference to System.Net.Http to declare client.
Dim client As HttpClient = New HttpClient()
' GetStringAsync returns a Task(Of String). That means that when you await the
' task you'll get a string (urlContents).
Dim getStringTask As Task(Of String) = client.GetStringAsync("https://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.
Dim urlContents As String = Await getStringTask
' The return statement specifies an integer result.
' Any methods that are awaiting AccessTheWebAsync retrieve the length value.
Return urlContents.Length
End Function
// 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("https://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;
}
如果 AccessTheWebAsync 没有它可以完成调用 GetStringAsync 和等待计算完成之间的任何工作,通过调用和等待简化代码在下面的单个语句。
Dim urlContents As String = Await client.GetStringAsync()
string urlContents = await client.GetStringAsync();
以下特征总结了使上面一个异步方法。
方法签名包含一个 Async 或 async 修饰符。
异步方法的名称以“Async”后缀,按照约定,关闭。
返回类型为下列类型之一:
Task<TResult>,如果您的方法具有操作个线程类型 TResult 的返回语句。
Task,如果方法没有返回语句或具有返回语句不操作。
无效 (在 Visual Basic 中 子 ),如果您编写一个异步事件处理程序。
有关更多信息,请参见“之后返回类型和参数”本主题。
方法通常包含至少一个等待表达式,指示个方法无法继续,直到该等待的异步操作完成的。同时,方法被挂起,并且,控件返回到方法的调用方。本主题的下一节将解释发生的悬挂点。
在异步方法,可以使用提供的关键字和类型指示要执行,因此,编译器执行方式,包括记录必须出现,当控件处于挂起的方法时回时间点。某些实例处理,例如循环,而异常处理,可能很难进行在传统异步代码的句柄。在异步方法,解决您编写这些元素,因为在一个同步解决方案会并将问题。
有关 asynchrony 的更多信息在 .NET framework 的早期版本,请参见 TPL 和传统 .NET 异步编程。
任何异步方法。
若要了解的最重要的操作在异步编程是控制流如何从方法移动到方法。下图通过处理导致生成。
在关系图的数值对应于以下步骤。
事件处理程序调用并等待 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。
当 AccessTheWebAsync 具有字符串结果时,方法可以计算该字符串的长度。然后 AccessTheWebAsync 工作也完成的,因此,等待事件处理程序可以继续。在完整的示例中的主题末尾,可以确认事件处理程序检索和打印长度结果的值。
如果您不熟悉异步编程,请需要一分钟考虑同步和异步行为之间的差异。一个同步方法返回,其工作完成 (第 5 步),但是,异步方法返回任务值,其工作挂起时 (第 3 步和第 6 步)。在异步方法最终完成其工作时,任务将会标记为已完成和结果,如果有,在任务中。
有关控制流的更多信息,请参见 异步程序中的控制流(C# 和 Visual Basic)。
API 异步方法
在什么情况下可能想知道找到支持异步编程的方法 (如 GetStringAsync。.NET Framework 4.5 包含与异步以及等待的许多成员。您可以通过附加到成员名称和 Task 或 Task<TResult>的返回类型“Async”后缀识别这些成员。例如,System.IO.Stream 选件类包含方法例如 CopyToAsync、ReadAsync和 WriteAsync 在同步方法 CopyTo、Read和 Write。
Windows 运行时 还包含您在 Windows 应用商店 apps 可以使用异步和等待的许多方法。有关更多信息和示例,请参见 快速入门:使用异步编程的时间运算符方法,异步编程 (windows 存储 apps)并 WhenAny:.NET Framework 和 Windows 运行时之间的桥接(C# 和 Visual Basic)。
线程
异步方法旨在成为非阻塞操作。当等待的任务运行时,在异步方法的一个等待表达式不会阻止当前线程。相反,该表达式注册该方法的其余部分作为继续并返回控制对异步方法的调用方。
异步和等待关键字不会导致其他线程创建。因为异步方法本身并不会运行的线程,异步方法不需要多线程。只有 + 当方法处于活动状态,则方法在当前同步上下文中运行并使用在线程的时间。可以使用 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(Of Integer)
Async Function TaskOfTResult_MethodAsync() As Task(Of Integer)
Dim hours As Integer
' . . .
' Return statement specifies an integer result.
Return hours
End Function
' Calls to TaskOfTResult_MethodAsync
Dim returnedTaskTResult As Task(Of Integer) = TaskOfTResult_MethodAsync()
Dim intResult As Integer = Await returnedTaskTResult
' or, in a single statement
Dim intResult As Integer = Await TaskOfTResult_MethodAsync()
' Signature specifies Task
Async Function Task_MethodAsync() As Task
' . . .
' The method has no return statement.
End Function
' Calls to Task_MethodAsync
Task returnedTask = Task_MethodAsync()
Await returnedTask
' or, in a single statement
Await Task_MethodAsync()
// Signature specifies Task<TResult>
async Task<int> TaskOfTResult_MethodAsync()
{
int hours;
// . . .
// Return statement specifies an integer result.
return hours;
}
// Calls to TaskOfTResult_MethodAsync
Task<int> returnedTaskTResult = TaskOfTResult_MethodAsync();
int intResult = await returnedTaskTResult;
// or, in a single statement
int intResult = await TaskOfTResult_MethodAsync();
// Signature specifies Task
async Task Task_MethodAsync()
{
// . . .
// The method has no return statement.
}
// Calls to Task_MethodAsync
Task returnedTask = Task_MethodAsync();
await returnedTask;
// or, in a single statement
await Task_MethodAsync();
每个返回的任务表示正在进行的工作。任务封装有关状态的信息异步过程和,最后,从进程的最终结果或处理引发的异常;如果未成功。
异步方法也是 Sub 方法 (Visual Basic) 或使 void 返回类型 (c# 中)。这将返回类型主要用于定义事件处理程序,void 返回类型需要。异步事件处理程序通常用作异步程序的起始点。
是 Sub 程序或具有 void 返回类型不能等待的异步方法和一个无效返回的方法的调用方无法捕获方法引发的任何异常。
异步方法不能声明在 Visual Basic 或 ref 的 ByRef 参数或在 C# 中 t3c3bfhx(v=vs.110).md 参数,但是,方法可以调用具有这些参数的方法。
有关更多信息和示例,请参见异步返回类型(C# 和 Visual Basic)。有关如何捕获异步方法的异常的更多信息,请参见 try-catch(C# 参考) 或 Try...Catch...Finally 语句 (Visual Basic)。
在 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 示例:从“异步编程的示例使用"和”等待。
' Add an Imports statement and a reference for System.Net.Http
Imports System.Net.Http
Class MainWindow
' Mark the event handler with async so you can use Await in it.
Private Async Sub StartButton_Click(sender As Object, e As RoutedEventArgs)
' Call and await separately.
'Task<int> getLengthTask = AccessTheWebAsync();
'' You can do independent work here.
'int contentLength = await getLengthTask;
Dim contentLength As Integer = Await AccessTheWebAsync()
ResultsTextBox.Text &=
String.Format(vbCrLf & "Length of the downloaded string: {0}." & vbCrLf, contentLength)
End Sub
' Three things to note in the signature:
' - The method has an Async modifier.
' - The return type is Task or Task(Of T). (See "Return Types" section.)
' Here, it is Task(Of Integer) because the return statement returns an integer.
' - The method name ends in "Async."
Async Function AccessTheWebAsync() As Task(Of Integer)
' You need to add a reference to System.Net.Http to declare client.
Dim client As HttpClient = New HttpClient()
' GetStringAsync returns a Task(Of String). That means that when you await the
' task you'll get a string (urlContents).
Dim getStringTask As Task(Of String) = client.GetStringAsync("https://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.
Dim urlContents As String = Await getStringTask
' The return statement specifies an integer result.
' Any methods that are awaiting AccessTheWebAsync retrieve the length value.
Return urlContents.Length
End Function
Sub DoIndependentWork()
ResultsTextBox.Text &= "Working . . . . . . ." & vbCrLf
End Sub
End Class
' Sample Output:
' Working . . . . . . .
' Length of the downloaded string: 41763.
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("https://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.