异步支持概述
C# 5 引入了两个关键字来简化异步编程:async 和 await。 这些关键字让你可以编写简单的代码,利用任务并行库在另一个线程中执行长期运行的操作(如网络访问),并在完成时轻松访问结果。 最新版本的 Xamarin.iOS 和 Xamarin.Android 支持 async 和 await - 本文档提供了有关将新语法与 Xamarin 配合使用的说明和示例。
Xamarin 的 Async 支持基于 Mono 3.0 基础构建,并将 API 配置文件从作为 Silverlight 的移动友好版本升级到 .NET 4.5 的移动友好版本。
概述
本文档介绍新的 async 和 await 关键字,然后演练了一些在 Xamarin.iOS 和 Xamarin.Android 中实现异步方法的简单示例。
有关 C# 5 的新异步功能的更完整讨论(包括大量示例和不同的使用方案),请参阅异步编程一文。
示例应用程序发起一个简单的异步 Web 请求(不阻止主线程),然后使用下载的 html 和字符计数更新 UI。
Xamarin 的 async 支持基于 Mono 3.0 基础构建,并将 API 配置文件从作为 Silverlight 的移动友好版本升级到 .NET 4.5 的移动友好版本。
要求
C# 5 功能需要 Xamarin.iOS 6.4 和 Xamarin.Android 4.8 中包含的 Mono 3.0。 系统将提示你升级 Mono、Xamarin.iOS、Xamarin.Android 和 Xamarin.Mac 以利用它。
使用 async 和 await
async
和 await
是新的 C# 语言功能,可与任务并行库结合使用,以便轻松编写线程代码来执行长期运行的任务,而无需阻止应用程序的主线程。
async
声明
async
关键字放在方法声明中(或 lambda 或匿名方法上),以指示它包含可异步运行的代码,即不阻止调用方的线程。
标记了 async
的方法应至少包含一个 await 表达式或语句。 如果方法中不存在任何 await
语句,则它将同步运行(与没有 async
修饰符相同)。 这也将导致编译器警告(但不会导致错误)。
返回类型
async 方法应返回 Task
、Task<TResult>
或 void
。
如果方法不返回任何其他值,请指定 Task
返回类型。
如果方法需要返回值,请指定 Task<TResult>
,其中 TResult
是返回的类型(例如 int
)。
void
返回类型主要用于需要它的事件处理程序。 调用 void-returning 异步方法的代码无法对结果 await
。
参数
异步方法无法声明 ref
或 out
参数。
await
await 运算符可以应用于标记为 async 的方法内的任务。 它会导致方法在该时间点停止执行,并等待任务完成。
使用 await 不会阻止调用方的线程,而会将控制权返回给调用方。 这意味着调用线程不会被阻止,因此,在等待任务时不会阻止用户界面线程。
任务完成后,该方法将继续在代码中的同一点执行。 这包括返回到 try-catch-finally 块的 try 范围(如果存在)。 await 不能在 catch 或 finally 块中使用。
详细了解 await。
异常处理
async 方法中发生的异常存储在任务中,并在任务被 await
时引发。 可以在 try-catch 块内捕获和处理这些异常。
取消
需要很长时间才能完成的异步方法应支持取消。 通常,取消的调用方式如下:
- 创建
CancellationTokenSource
对象。 CancellationTokenSource.Token
实例被传递给可取消的异步方法。- 通过调用
CancellationTokenSource.Cancel
方法请求取消。
然后,该任务自行取消并确认取消。
有关取消的详细信息,请参阅微调异步应用程序 (C#)。
示例
下载示例(适用于 iOS 和 Android),查看移动应用中 async
和 await
的工作示例。 本部分更详细地讨论了示例代码。
编写 async 方法
以下方法演示如何使用 await
任务对 async
方法进行编码:
public async Task<int> DownloadHomepage()
{
var httpClient = new HttpClient(); // Xamarin supports HttpClient!
Task<string> contentsTask = httpClient.GetStringAsync("https://visualstudio.microsoft.com/xamarin"); // async method!
// await! control returns to the caller and the task continues to run on another thread
string contents = await contentsTask;
ResultEditText.Text += "DownloadHomepage method continues after async call. . . . .\n";
// After contentTask completes, you can calculate the length of the string.
int exampleInt = contents.Length;
ResultEditText.Text += "Downloaded the html and found out the length.\n\n\n";
ResultEditText.Text += contents; // just dump the entire HTML
return exampleInt; // Task<TResult> returns an object of type TResult, in this case int
}
请注意以下几点:
- 该方法声明包含
async
关键字。 - 返回类型为
Task<int>
,因此调用代码可以访问此方法中计算的int
值。 - return 语句是
return exampleInt;
,它是一个整数对象,该方法返回Task<int>
是语言改进的一部分。
调用 async 方法 1
可在 Android 示例应用程序中找到此按钮单击事件处理程序,以调用上述方法:
GetButton.Click += async (sender, e) => {
Task<int> sizeTask = DownloadHomepage();
ResultTextView.Text = "loading...";
ResultEditText.Text = "loading...\n";
// await! control returns to the caller
var intResult = await sizeTask;
// when the Task<int> returns, the value is available and we can display on the UI
ResultTextView.Text = "Length: " + intResult ;
// "returns" void, since it's an event handler
};
注意:
- 匿名委托具有 async 关键字前缀。
- 异步方法 DownloadHomepage 返回存储在 sizeTask 变量中的 Task<int>。
- 代码等待 sizeTask 变量。 这是方法暂停且控制返回到调用代码的位置,直到异步任务在其自己的线程上完成。
- 当任务在方法的第一行创建时,执行不会暂停,即使任务在那里创建。 await 关键字表示暂停执行的位置。
- 异步任务完成后,从 await 行设置 intResult 并在原始线程上继续执行。
调用 async 方法 2
在 iOS 示例应用程序中,此示例的编写方式略有不同,这是为了演示替代方法。 此示例不使用匿名委托,而是声明一个 async
事件处理程序,该程序的分配类似于常规事件处理程序:
GetButton.TouchUpInside += HandleTouchUpInside;
然后,定义该事件处理程序方法,如下所示:
async void HandleTouchUpInside (object sender, EventArgs e)
{
ResultLabel.Text = "loading...";
ResultTextView.Text = "loading...\n";
// await! control returns to the caller
var intResult = await DownloadHomepage();
// when the Task<int> returns, the value is available and we can display on the UI
ResultLabel.Text = "Length: " + intResult ;
}
一些要点:
- 该方法标记为
async
但返回void
。 这通常仅适用于事件处理程序(否则会返回Task
或Task<TResult>
)。 DownloadHomepage
方法上的await
关键字直接分配给变量 (intResult
),与上一个示例不同,在那里我们使用中间Task<int>
变量来引用任务。 这是控制返回到调用方的位置,直到异步方法在另一个线程上完成。- 异步方法完成并返回时,执行将在
await
恢复,这意味着将返回整数结果,然后在 UI 小组件中呈现。
总结
使用 async 和 await 极大地简化了在后台线程上生成长期运行的操作所需的代码,而不会阻止主线程。 此外,它还使得在任务完成时访问结果变得轻松。
本文档概览了 Xamarin.iOS 和 Xamarin.Android 的新语言关键字和示例。