建立Worker 前面我们看到了一个简单的Worker类。我们已经定义IWorker&&接口,现在能利用已经建立的Controller来增强该类。
首先建立Background.dll文件。这一步很重要,如果没有的话,我们在建立测试窗体时,ActivityBar控件不会在Toolbox上出现。
给解决方案添加一个叫bgTest的Windows窗体应用程序项目,将它设置为启动项目。
接着使用Add References 对话框的 Projects页来添加对Background项目的引用。
Imports Background
Public Class Worker Implements IWorker Private mController As IController Private mInner As Integer Private mOuter As Integer Public Sub New(ByVal InnerSize As Integer, ByVal OuterSize As Integer) mInner = InnerSize mOuter = OuterSize End Sub '被controller调用,这样可以得到一个controller的&&keyword=%D6%B8%D5%EB&Submit=+%CB%D1%CB%F7+">指针。
Private Sub Init(ByVal Controller As IController) _ Implements IWorker.Initialize mController = Controller End Sub
Private Sub Work() Implements IWorker.Start Dim innerIndex As Integer Dim outerIndex As Integer Dim value As Double Try For outerIndex = 0 To mOuter If mController.Running Then mController.Display("Outer loop " & outerIndex & " starting") mController.SetPercent(CInt(outerIndex / mOuter * 100)) Else '有"取消"请求 mController.Completed(True) Exit Sub End If
For innerIndex = 0 To mInner '在此处作一些cool运算 value = Math.Sqrt(CDbl(innerIndex - outerIndex)) Next Next mController.SetPercent(100) mController.Completed(False) Catch e As Exception mController.Failed(e) End Try
End Sub
End Class
|
我们添加了Init方法来执行IWorker.Initialize。Controller调用该方法,这样就有了一个Controller对象的&&keyword=%D6%B8%D5%EB&Submit=+%CB%D1%CB%F7+">指针。
我们将Work方法改为私有(Private),仅仅用于执行IWorker.Start方法。该方法将在工作线程上运行。
Work方法使用Try..Catch块得到了加强,这样我们能捕捉任何错误并使用Controller的Failed方法将错误返回到UI。
假定代码能运行,在该代码运行时,我们调用Controller对象的Display和SetPercent方法来更新状态和完成百分比。
我们也周期性地检查Controller对象的Running&&属性来查看是否有"取消"请求。如果有,就停止处理并显示由于有"取消"请求而完成。
建立显示窗体
最后建立一个窗体来启动和取消后台处理。它也显示活动和状态信息。
给窗体添加两个按钮(btnStart和btnRequestCancel)、两个label(Label1和Label2)、一个ProgressBar(ProgressBar1)和ActivityBar (ActivityBar1),如图7所示:

图7:Form1的布局
窗体需要执行IClient,这样Controller控件才能与它交互。
Imports Background Public Class Form1 Inherits System.Windows.Forms.Form Implements IClient窗体也需要一个Controller对象和一个标志来追踪后台处理是活动的或完成了。
Private mController As New Controller(Me) Private mActive As Boolean
接着我们添加方法来实现IClient中定义的&&接口。
#Region " IClient "
Private Sub TaskStarted(ByVal Controller As Controller) _ Implements IClient.Start
mActive = True
Label1.Text = "Starting" Label2.Text = "0%" ProgressBar1.Value = 0 ActivityBar1.Start() End Sub
Private Sub TaskStatus(ByVal Text As String) _ Implements IClient.Display
Label1.Text = Text Label2.Text = CStr(mController.Percent) & "%" ProgressBar1.Value = mController.Percent
End Sub
Private Sub TaskFailed(ByVal e As Exception) _ Implements IClient.Failed
ActivityBar1.Stop()
Label1.Text = e.Message MsgBox(e.ToString) mActive = False
End Sub
Private Sub TaskCompleted(ByVal Cancelled As Boolean) _ Implements IClient.Completed
Label1.Text = "Completed" Label2.Text = CStr(mController.Percent) & "%" ProgressBar1.Value = mController.Percent ActivityBar1.Stop() mActive = False End Sub #End Region
|
注意所有的代码都没有处理线程。每部分都包含监视后台处理的状态时可以作适当的响应的代码。每次我们都更新过程状态信息的显示、它的完成百分比(在文本框和ProgressBar中)并启动和停止ActivityBar控件。
标志mActive很重要。当工作线程活动时,如果用户关闭了窗体,程序可能挂起或者不稳定。为了避免这种情况,如果后台处理是激活的,我们截取窗体的Closing&&事件并在后台处理活动时终止关闭的企图。
Private Sub Form1_Closing(ByVal sender As Object, _ ByVal e As System.ComponentModel.CancelEventArgs) _ Handles MyBase.Closing e.Cancel = mActive End Sub |
在本例中我们选择初始化"取消"操作,这依赖于具体的应用程序需求。
下面的代码用于实现按钮的Click&&事件:
Private Sub btnStart_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles btnStart.Click mController.Start(New Worker(2000000, 100)) End Sub
Private Sub btnStop_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles btnStop.Click
Label1.Text = "Cancelling ..." mController.Cancel() End SubStart |
按钮简单地调用Controller的Start方法,将Worker对象的一个实例传递给它。
为了使它运行得更有趣,你也许要调整初始化Worker对象得值。本文中的值在两个P3/450得计算机上运行很好。实际的Worker对象执行更有意思的工作,但也是长时间的处理。
Cancel按钮调用Controller对象Cancel方法,并更新显示来表明有"取消"请求。这仅仅是个"取消"请求,在工作实际停止前也许有一段时间。比较好的方法是给用户一些回应,至少表明点击按钮已经被注意到了。
运行该程序。Start按钮按下时,Worker启动了,随着它的运行显示在发生变化。你可以在屏幕上移动窗体并与窗体交互,因为UI线程本质上是空闲的,准备好了与你交互。
同时,工作线程正在后台处理繁忙工作,给UI线程发送周期性的状态更新消息以供显示。
结论
多线程是个非常强大的工具,在任何需要长时间运行的事务中都能使用。我们可以用它运行工作代码而不停止用户界面。同时,多线程的使用可能变得难以想象的复杂,更加难于调试。
但这不总是可行的,我们应该力争给每个工作线程提供一组该线程操作的独立数据。最简单的实现方法是为每个线程创建一个对象,该对象包含线程需要操作的数据和工作的代码。
通过实现一个结构化的框架作为工作线程与UI线程的中介,我们难以想像地简化了编写多线程工作代码和控制它的UI代码。本文中我有一个框架实例,你能改变它使之适应你的应用程序需求。