'' 启动辅助线程
Public Sub Start(Optional ByVal Worker As IWorker = Nothing)
'' 如果辅助线程已经启动,将产生错误
If mRunning Then
Throw New Exception("Background process already running")
End If
mRunning = True
'' 存储对辅助对象的引用,并
'' 初始化辅助对象,使其包含
'' 对 Controller 的引用
mWorker = Worker
mWorker.Initialize(Me)
'' 创建后台线程
'' 以进行后台操作
Dim backThread As New Thread(AddressOf mWorker.Start)
'' 开始后台工作
backThread.Start()
'' 告诉客户端后台工作已开始
CType(mClient, IClient).Start(Me)
End Sub
'' 此代码由 UI 调用,因此在 UI
'' 线程上运行。它只设置了请求
'' 取消的标志
Public Sub Cancel()
mRunning = False
End Sub
'' 返回完成百分比值,并且
'' 只被 UI 线程调用
Public ReadOnly Property Percent() As Integer
Get
Return mPercent
End Get
End Property
#End Region
此处唯一比较特殊的代码位于 Start 方法中,我们可以在该方法中创建辅助线程然后启动该线程:
Dim backThread As New Thread(AddressOf mWorker.Start)
backThread.Start()
要创建线程,需要在 Worker 对象的 IWorker 接口上传递 Start 方法的地址。然后,只需调用线程对象的 Start 方法即可开始操作。此时我们要特别注意,UI 不应直接与 Worker 交互,Worker 也不应直接与 UI 交互。
请注意,Cancel 方法只设置一个标志,表明我们不希望继续运行。辅助代码应定期查看此标志,以确定是否应该停止运行。
现在,我们可以实现 Worker 对象运行时将由辅助线程调用的代码。此代码比较有趣,因为它必须将 Display 和 Completed 从辅助线程中转至 UI 线程,同时还要在 UI 线程上完成此操作。
要完成此操作,我们可以使用 Form 对象的 Invoke 方法。此方法接受窗体应该调用的方法的委托指针,以及包含该方法的参数的 Object 类型数组。
Invoke 方法不直接调用窗体上的方法,而是请求窗体返回并使用窗体的 UI 线程调用该方法。此操作可通过向窗体发送 Windows 消息在后台完成。这说明窗体获得这些方法调用的方式与从操作系统中获得 click 或 keypress 事件的方式基本相同。
通常,这些细节不会影响大局。结果由 Invoke 方法触发一个进程,通过该进程窗体将终止其 UI 线程上运行的方法,这就是我们要实现的目标。
再次重申,此代码位于 Region 内,目的是为了明确它将在辅助线程上调用:
#Region " 从辅助线程调用的代码 "
'' 从辅助线程调用,以更新显示
'' 这将触发对包含状态文本的 UI 的
'' 方法调用 - 该调用是在 UI 线程上
'' 进行的
Private Sub Display(ByVal Text As String) _
Implements IController.Display
Dim disp