4. 异步程序中的控制流 (C#)¶
可以使用 async 和 await 关键字更加轻松地编写和维护异步程序。 但是,如果不了解程序的运行方式,结果可能会让你大吃一惊。 此主题通过一个简单的异步程序跟踪控制流,以显示控制从一种方法移动到另一种方法的情况,以及每次所传输的信息。
一般情况下,使用 async (C#) 修饰符标记包含异步代码的方法。 在使用 async 修饰符标记的方法中,可以使用 await (C#) 运算符来指定暂停该方法以等待调用的异步进程完成的位置。 有关详细信息,请参阅使用 Async 和 Await 的异步编程 (C#)。
下面的示例使用异步方法以字符串的形式下载指定网站的内容,并显示该字符串的长度。 此示例包含以下两种方法。
startButton_Click,它调用 AccessTheWebAsync 并显示结果。
AccessTheWebAsync,它以字符串的形式下载网站的内容,并返回该字符串的长度。 AccessTheWebAsync 使用异步 HttpClient 方法 GetStringAsync(String) 来下载内容。
编号的显示行显示在整个程序的策略点处,以帮助你了解程序的运行方式和说明被标记的每个点处发生的情况。 显示行标记为“1”到“6”。 该标签表示程序到达这些代码行的顺序。
下面的代码显示该程序的概要。
每个标记位置(“1”到“6”)显示有关该程序的当前状态的信息。 将生成以下输出:
ONE: Entering startButton_Click.
Calling AccessTheWebAsync.
TWO: Entering AccessTheWebAsync.
Calling HttpClient.GetStringAsync.
THREE: Back in AccessTheWebAsync.
Task getStringTask is started.
About to await getStringTask & return a Task<int> to startButton_Click.
FOUR: Back in startButton_Click.
Task getLengthTask is started.
About to await getLengthTask -- no caller to return to.
FIVE: Back in AccessTheWebAsync.
Task getStringTask is complete.
Processing the return statement.
Exiting from AccessTheWebAsync.
SIX: Back in startButton_Click.
Task getLengthTask is finished.
Result from AccessTheWebAsync is stored in contentLength.
About to display contentLength and exit.
Length of the downloaded string: 33946.
5. 取消任务和处理已完成任务¶
可以使用由 Task 类型提供的方法和属性将精度和灵活性添加到异步应用程序。 本部分中的主题介绍使用 CancellationToken 的示例和一些重要的 Task 方法,例如 Task.WhenAll 和 Task.WhenAny。
使用 WhenAny 和 WhenAll 可以更轻松地启动多个任务并通过监视单个任务待其完成。
集合中的任何任务完成时,WhenAny 将返回完成的任务。
集合中的所有任务完成时,WhenAll 将返回完成的任务。
5.1. 取消异步任务或任务列表¶
如果不想等待异步应用程序完成,可以设置一个按钮用来取消它。 通过遵循本主题中的示例,可以为下载一个或一组网站内容的应用程序添加一个取消按钮。
5.2. 在一段时间后取消异步任务¶
如果不希望等待操作结束,可使用 CancellationTokenSource.CancelAfter 方法在一段时间后取消异步操作。 此方法会计划取消未在 CancelAfter 表达式指定的时间段内完成的任何关联任务。
5.3. 在完成一个异步任务后取消剩余任务¶
通过结合使用 Task.WhenAny 方法和 CancellationToken,可在一个任务完成时取消所有剩余任务。 WhenAny 方法采用任务集合中的一个参数。 该方法启动所有任务,并返回单个任务。 当集合中任意任务完成时,完成单个任务。
5.4. 启动多个异步任务并在其完成时进行处理¶
通过使用 Task.WhenAny,可以同时启动多个任务,并在它们完成时逐个对它们进行处理,而不是按照它们的启动顺序进行处理。
6. 处理异步应用中的重新进入¶
在应用中包含异步代码时,应考虑并且可以阻止重新进入(指在异步操作完成之前重新进入它)。 如果不识别并处理重新进入的可能性,则它可能会导致意外结果。