们可以实现此目标,还能实现一个能够通过某种方式实现复杂代码的架构,可以用来管理或控制后台线程及其 UI 交互。
我们先来讨论体系结构,然后再讨论如何设计和实现代码。从本文的相关
链接可以
下载此代码以及说明如何使用此代码的示例应用程序。
通常情况下,应用程序中首先会启动一个单一线程,来打开用户界面。我们将其命名为“UI 线程”以便于理解。“UI 线程”是许多应用程序中的唯一线程,因此它要处理 UI 并完成所有操作。
但是,现在我们创建一个“辅助线程”进行某些后台操作,让 UI 线程集中处理用户界面。这样即使辅助线程繁忙,UI 线程也可以对用户保持响应状态。
我们在 UI 线程和辅助线程之间插入一层代码,使其充当 UI 和辅助代码之间的接口。此代码实质上是一个“控制器”,用来管理和控制辅助线程及其与 UI 之间的交互。
图 1:UI 线程、控制器和辅助线程
控制器包含的代码可以安全地启动辅助线程,将任何状态消息从辅助线程中转给 UI 线程,以及将任何取消请求从 UI 线程中转回辅助线程。UI 代码和辅助代码不能直接交互,它们通常要通过控制器的代码进行交互。
但是辅助线程被激活“之前”和“之后”的时间段除外,这时 UI 代码可以与 Worker 对象进行交互。启动辅助线程之前,UI 可以创建并初始化 Worker 对象。终止辅助线程之后,UI 可以从 Worker 对象中检索任何值。从 UI 的角度看,将形成以下事件流:
创建 Worker 对象。
初始化 Worker 对象。
调用 Controller 以启动辅助线程。
Worker 对象将通过 Controller 将状态信息发送给 UI。
UI 可以通过 Controller 将取消请求发送给 Worker 对象。
Worker 对象在完成操作后通过 Controller 通知 UI。
值可以直接从 Worker 对象中检索。
除了在辅助线程处于激活状态时 UI 代码无法与 Worker 对象直接交互的限制外,对 UI 没有特殊的编码要求。即使正在运行后台操作,UI 也会对用户保持激活和响应状态。
从 Worker 对象的角度看,将形成以下事件流:
UI 代码创建 Worker 对象。
UI 代码使用所需的数据初始化 Worker 对象。
Controller 创建后台线程并调用 Worker 对象的方法。
Worker 对象运行辅助代码。
Worker 对象将状态信息传递给 Controller,以便 Controller 将状态信息中转给 UI。
Worker 对象适时检查是否存在取消请求,如果存在,则停止运行。
Worker 对象完成后,告诉 Controller 已完成,以便 Controller 将该信息中转给 UI。
现在辅助线程已终止,因此 UI 可以与 Worker 对象直接交互。
由于辅助代码只与 Controller 交互,因此我们不必担心辅助线程会意外地与 UI 组件交互(这无疑会使应用程序不稳定)。现在,辅助代码依靠 Controller 与 UI 线程进行正确通信,因此各项操作都很安全。
这意味着,只要处理好 Worker 对象中的实例变量,就无需处理辅助代码中的任何线程问题。
使用图表通常能够很好地了解不同组件(尤其是不同线程上的组件)之间的交互。Microsoft® Visio® 支持创建 UML(通用建模语言)图表,对理解很有帮助。
以下是说明 UI、Worker 对象和 Controller 之间事件流的 UML 序列图表。此图表假设不存在任何取消操作请求。Worker 和 Controller 对象下面重叠在垂直线上的垂直活动栏突出了辅助线程上运行的代码。其他所有代码都在 UI 线程上运行。
图 2:说明进程流的序列图表
使用 UML 活动图表也可以查看事件流。这种图表形式的着重点在于任务而不是对象,因此其中显示了发生的一系列步骤以及各步骤之间的流程。我们很容易看出 UI 代码如何停留在左侧的线程中,而 Worker 对象如何在右侧的线程上工作。Worker 对象在其他线程中运行之前和运行之后可以直接由 UI 使用,以便初始化值,然后再检索结果。
图 3:显示进程流的活动图表