示例代码需要访问假想的股票报价Web服务。我们在Visual Studio .NET 2003中建立一个Web服务,把它命名为"StockWS"。这个Web服务由一个叫做getPRice()的Web方法组成,该方法只接受一个股票编码参数:
Public Function getPrice(ByVal stock As String) As Single Return Rnd() * 100 End Function 不管被请求的股票是什么,getPrice()方法都生成一个随机的价格。它的唯一目标是模拟一个返回特定股票价格的真实的Web服务。 尽管本文使用的是一个成型的Web服务来进行演示的,但是你可以轻易地替换这个Web服务以显示真正的股票信息。
Private Sub Chart1_Load(ByVal sender As _ System.Object, ByVal e As System.EventArgs) Handles Chart1.Load 'x轴上每隔5点显示时间 Chart1.AxisX.Step = 5 '每个点之间用5象素间隔 Chart1.AxisX.PiXPerUnit = 5 '使图表可以滚动 Chart1.Scrollable = True '打开和关闭通讯管道 Chart1.OpenData(COD.Values, 1, COD.Unknown) Chart1.CloseData(COD.Values) End Sub 给当前的窗体添加一个叫做StockQuote的类。StockQuote类调用前面的Web服务并用返回的股票价格来更新图表。
Public Class StockQuote '组件中图形的数量 Const NUM_SERIES = 1
Private lastPoint As Integer = 0 Dim stockPrice As Single
Private pStockSymbol As String Private pStockSeries As Integer = 0 Private pChartControl As Chart
WriteOnly Property StockSymbol() Set(ByVal Value) pStockSymbol = Value End Set End Property
WriteOnly Property ChartControl() Set(ByVal Value) pChartControl = Value End Set End Property
Public Sub InvokeWebService() Dim ws As New StockWS.Service1
For i As Integer = 0 To 10000 stockPrice = ws.getPrice(pStockSymbol) pChartControl.Invoke(New _ myDelegate(AddressOf updateChart), New Object() {}) '继续之前等待1秒钟 Thread.Sleep(1000) Next End Sub
Public Delegate Sub myDelegate() Public Sub updateChart() pChartControl.OpenData(COD.Values, NUM_SERIES, COD.Unknown) pChartControl.Value(pStockSeries, lastPoint) = stockPrice '显示x轴上的时间 pChartControl.AxisX.Label(lastPoint) = DateTime.Now.ToShortTimeString lastPoint += 1 pChartControl.CloseData(COD.Values) '把滚动条移到最右边 pChartControl.AxisX.ScrollPosition = pChartControl.AxisX.ScrollSize End Sub
End Class 你通过StockSymbol属性把需要的股票编码传递给StockQuote类,并使用ChartControl属性设置图表更新。InvokeWebService()方法在循环(示例中设置为10,000)中周期性地调用上面的Web服务。由于这个类会在一个单独的线程中执行,你必须非常小心以确保自己不会自动地更新某个Windows控件,因为Windows控件并不是线程安全的(thread-safe)。作为代替,你必须使用委托并调用自己希望更新的控件上的Invoke()方法。代码每秒钟调用Web服务一次,这是由Thread.Sleep(1000)语句设置的。
Private Sub BTnGetStockQuote1_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles btnGetStockQuote1.Click Dim sq As New StockQuote sq.StockSymbol = cmbStocks1.SelectedItem sq.ChartControl = Chart1 t1 = New Thread(AddressOf sq.InvokeWebService) t1.Start() End Sub 把调用该Web服务的代码打包为一个类的主要原因是Thread类构造函数只能接受一个ThreadStart委托(启动线程的方法的委托),不存在可以接受多个参数值的重载的Thread.Start()方法。因此,把多个参数传递到一个线程中的唯一途径是把调用的相关代码打包为一个类,接着你就可以通过这个类的参数来传递参数。
Dim t1, t2 As Thread 示例项目使用计时器控件(Timer,在工具箱中)来显示第二个线程的状态信息。把计时器拖放到窗体上,并把它的Interval属性设置为500,这使该计时器的Tick事件每半秒钟(500毫秒)调用一次。Tick事件处理程序中的代码更新了标签控件lblThreadStatus中的线程状态信息:
Private Sub Timer1_Tick(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles Timer1.Tick lblThreadStatus.Text = "Thread state: " & _ t2.ThreadState.ToString End Sub 第二个图表也使用与第一个图表相同的初始化代码:
Private Sub Chart2_Load(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles Chart2.Load '在x轴上每5点显示时间 Chart2.AxisX.Step = 5 '每个点之间用5个象素分隔 Chart2.AxisX.PixPerUnit = 5 '使图表可以滚动 Chart2.Scrollable = True '打开和关闭通讯管道- Chart2.OpenData(COD.Values, 1, COD.Unknown) Chart2.CloseData(COD.Values) End Sub 你点击第二个图表的"获取股票报价"按钮的时候,代码建立一个新的线程--同时激活计时器,这样窗体才能够显示线程的状态信息:
Private Sub btnGetStockQuote2_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles btnGetStockQuote2.Click Dim sq As New StockQuote sq.StockSymbol = cmbStocks2.SelectedItem sq.ChartControl = Chart2 t2 = New Thread(AddressOf sq.InvokeWebService) t2.Start()
'激活暂停和停止按钮 btnPauseContinue.Enabled = True btnStop.Enabled = True '激活计时器控件 Timer1.Enabled = True End Sub 按F5测试这两个图表(图7所示)。为每个图表选择一只股票,你将看到这两个图表同步显示。
Private Sub btnPauseContinue_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles btnPauseContinue.Click ' 假如线程处于睡眠和运行状态就挂起它 If t2.ThreadState = ThreadState.WaitSleepJoin _ Or t2.ThreadState = ThreadState.Running Then t2.Suspend() btnPauseContinue.Text = "Continue" Else ' 继续该线程 t2.Resume() btnPauseContinue.Text = "Pause" End If End Sub 停止线程则使用Abort()方法:
Private Sub btnStop_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles btnStop.Click Try If Not t2.ThreadState = ThreadState.Stopped Then btnPauseContinue.Enabled = False btnStop.Enabled = False t2.Abort() End If Catch ex As Exception MsgBox(ex.ToString) End Try End Sub 通过运行示例项目,你会发现自己已经能够使用多线程技术建立应用程序,使应用程序在执行后台事务的时候,仍然保持响应。尽管本文的示例使用的是Web服务,但是相同的原则也可以应用于其它类型的后台事务。例如,你可以改变这个应用程序以读取外部设备(例如温度计或血压计监视设备)的数据。