异步支持概述

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。

示例应用程序发起一个简单的异步 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

asyncawait 是新的 C# 语言功能,可与任务并行库结合使用,以便轻松编写线程代码来执行长期运行的任务,而无需阻止应用程序的主线程。

async

声明

async 关键字放在方法声明中(或 lambda 或匿名方法上),以指示它包含可异步运行的代码,即不阻止调用方的线程。

标记了 async 的方法应至少包含一个 await 表达式或语句。 如果方法中不存在任何 await 语句,则它将同步运行(与没有 async 修饰符相同)。 这也将导致编译器警告(但不会导致错误)。

返回类型

async 方法应返回 TaskTask<TResult>void

如果方法不返回任何其他值,请指定 Task 返回类型。

如果方法需要返回值,请指定 Task<TResult>,其中 TResult 是返回的类型(例如 int)。

void 返回类型主要用于需要它的事件处理程序。 调用 void-returning 异步方法的代码无法对结果 await

参数

异步方法无法声明 refout 参数。

await

await 运算符可以应用于标记为 async 的方法内的任务。 它会导致方法在该时间点停止执行,并等待任务完成。

使用 await 不会阻止调用方的线程,而会将控制权返回给调用方。 这意味着调用线程不会被阻止,因此,在等待任务时不会阻止用户界面线程。

任务完成后,该方法将继续在代码中的同一点执行。 这包括返回到 try-catch-finally 块的 try 范围(如果存在)。 await 不能在 catch 或 finally 块中使用。

详细了解 await

异常处理

async 方法中发生的异常存储在任务中,并在任务被 await 时引发。 可以在 try-catch 块内捕获和处理这些异常。

取消

需要很长时间才能完成的异步方法应支持取消。 通常,取消的调用方式如下:

  • 创建 CancellationTokenSource 对象。
  • CancellationTokenSource.Token 实例被传递给可取消的异步方法。
  • 通过调用 CancellationTokenSource.Cancel 方法请求取消。

然后,该任务自行取消并确认取消。

有关取消的详细信息,请参阅微调异步应用程序 (C#)

示例

下载示例(适用于 iOS 和 Android),查看移动应用中 asyncawait 的工作示例。 本部分更详细地讨论了示例代码。

编写 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。 这通常仅适用于事件处理程序(否则会返回 TaskTask<TResult>)。
  • DownloadHomepage 方法上的 await 关键字直接分配给变量 (intResult),与上一个示例不同,在那里我们使用中间 Task<int> 变量来引用任务。 这是控制返回到调用方的位置,直到异步方法在另一个线程上完成。
  • 异步方法完成并返回时,执行将在 await 恢复,这意味着将返回整数结果,然后在 UI 小组件中呈现。

总结

使用 async 和 await 极大地简化了在后台线程上生成长期运行的操作所需的代码,而不会阻止主线程。 此外,它还使得在任务完成时访问结果变得轻松。

本文档概览了 Xamarin.iOS 和 Xamarin.Android 的新语言关键字和示例。