用户期望应用在计算时保持响应状态,而不考虑计算机类型。 这对不同的应用意味着不同的事情。 对于某些人,这意味着提供更逼真的物理模拟、更快地从磁盘或网络加载数据、快速呈现复杂的场景并在页面之间导航、快速找到路线或迅速处理数据。 无论计算类型如何,用户都希望其应用对其输入执行操作,并消除在应用“思考”时似乎无响应的实例。
应用是事件驱动的,这意味着你的代码在响应事件时执行工作,然后它处于空闲状态,直到下一个事件为止。 UI 的平台代码(布局、输入、引发事件等)和应用的 UI 代码均在同一 UI 线程上执行。 一次只能执行一个指令,因此,如果应用代码处理事件需要太长的时间,框架将无法运行布局或引发表示用户交互的新事件。 应用的响应能力与处理工作的 UI 线程的可用性相关。
需要使用 UI 线程对 UI 线程进行几乎所有更改,包括创建 UI 类型并访问其成员。 无法从后台线程更新 UI,但可以使用 CoreDispatcher.RunAsync 向 UI 发布消息,导致代码在那里运行。
注意 一个例外是,有一个单独的呈现线程可以应用 UI 更改,不会影响输入的处理方式或基本布局。 例如,许多不影响布局的动画和过渡都可以在此呈现线程上运行。
延迟元素实例化
应用中一些最慢的阶段可能包括启动和切换视图。 不要进行多于必要的工作以便展示用户最初看到的界面。 例如,不要为逐步展现的 UI 和弹出窗口的内容创建用户界面。
- 使用 x:Load 属性 或 x:DeferLoadStrategy 来延迟实例化元素。
- 以编程方式将元素插入到按需树中。
CoreDispatcher.RunIdleAsync 队列适用于 UI 线程在不忙时进行处理。
使用异步 API
为了帮助你的应用保持响应,该平台提供了许多 API 的异步版本。 异步 API 可确保您的活动执行线程不会被长时间阻塞。 从 UI 线程调用 API 时,请使用异步版本(如果可用)。 有关使用异步模式编程的详细信息,请参阅 C# 或 Visual Basic 中的异步编程或调用异步 API。
将工作卸载到后台线程
编写事件处理程序以快速返回。 在需要执行大量复杂工作的情况下,请将其安排在后台线程上,然后返回。
可以使用 C# 中的 await 运算符、Visual Basic 中的 Await 运算符或 C++ 中的委托来异步计划工作。 但这不能保证计划的工作将在后台线程上运行。 许多通用 Windows 平台 (UWP) API 会自动在后台线程中安排任务,但如果您仅使用 await 或委托来调用应用代码,那么您将会在 UI 线程上运行该委托或方法。 必须显式说明何时要在后台线程上运行应用代码。 在 C# 和 Visual Basic 中,可以通过将代码传递给 Task.Run 来实现此目的。
请记住,只能从 UI 线程访问 UI 元素。 在启动后台工作之前,使用 UI 线程访问 UI 元素,/或使用 CoreDispatcher.RunAsync 或后台线程上的 CoreDispatcher.RunIdleAsync。
在后台线程上可以执行的工作示例是计算游戏中的电脑 AI。 计算计算机的下一步移动的代码可能需要花费很多时间才能执行。
public class AsyncExample
{
private async void NextMove_Click(object sender, RoutedEventArgs e)
{
// The await causes the handler to return immediately.
await System.Threading.Tasks.Task.Run(() => ComputeNextMove());
// Now update the UI with the results.
// ...
}
private async System.Threading.Tasks.Task ComputeNextMove()
{
// Perform background work here.
// Don't directly access UI elements from this method.
}
}
Public Class AsyncExample ' ... Private Async Sub NextMove_Click(ByVal sender As Object, ByVal e As RoutedEventArgs) Await Task.Run(Function() ComputeNextMove()) ' update the UI with results End Sub Private Async Function ComputeNextMove() As Task ' ... End Function ' ... End Class
在此示例中, NextMove_Click
处理程序在 await 处返回,以使 UI 线程保持响应。 但在后台线程上运行的 ComputeNextMove
完成后,执行会再次回到该处理程序中。 在处理程序中,剩余的代码会用结果更新用户界面。
注意 还有一个适用于 UWP 的 ThreadPool 和 ThreadPoolTimer API,可用于类似场景。 有关详细信息,请参阅 线程和异步编程。