本文的內容是學習如何使用多線程技術建立應用程序
使應用程序在執行時間和資源密集型後台事務的時候
用戶界面(UI)仍然保持活動狀態
多線程技術(multithreading)是編程中最強大的概念之一
使用多線程技術
你可以把復雜的事務拆分到彼此獨立執行的多個線程之中
良好的多線程應用程序是自然地同步的
類似於Web服務調用
在默認情況下
Web服務調用屬於阻塞(blocking)調用
即調用者(caller)的代碼停止執行
直到Web服務返回結果為止
但是由於Web服務調用通常很慢
就可能導致客戶端性能降低
除非你采用特殊的步驟使調用異步進行
本文講解的是如何建立一個圖表應用程序
從這個例子中你可以看到如何在不影響客戶端UI的時候異步地調用Web服務
示例代碼利用Chart FX組件使用圖形來顯示股票信息
當然讀者也可以使用
NET編寫的免費圖表類庫
建立一個Web服務 示例代碼需要訪問假想的股票報價Web服務
我們在Visual Studio
NET
中建立一個Web服務
把它命名為
StockWS
這個Web服務由一個叫做getPrice()的Web方法組成
該方法只接受一個股票編碼參數
Public Function getPrice(ByVal stock As String) As Single
Return Rnd() *
End Function
不管被請求的股票是什麼
getPrice()方法都生成一個隨機的價格
它的唯一目標是模擬一個返回特定股票價格的真實的Web服務
盡管本文使用的是一個成型的Web服務來進行演示的
但是你可以輕易地替換這個Web服務以顯示真正的股票信息
使用Chart FX組件顯示圖形 在建立上面的Web服務項目之後
先給解決方案浏覽器添加一個Windows應用程序項目(叫做Stock Quote
股票報價)
給該項目增加一個對前面所建立的Web服務的引用
解決方案浏覽器現在應該如圖
所示
圖
解決方案浏覽器中的項目-圖中顯示了StockWS Web服務項目和
Windows窗體項目Stock Quote 為了建立本文的示例項目
你必須從下載和安裝Chart FX組件
天試用版
在安裝這個繪圖組件之後
你可以在Visual Studio
NET
的工具盒中看到它(如圖
所示)
圖
工具盒中的Chart組件
你需要從網站上下載並安裝Chart FX組件
天試用版
在該Windows應用程序默認的Form
中
用下面一些控件填充該窗體
如圖
所示
· Chart
· ComboBox
· Button
圖
Stock Quote主窗體-圖中顯示了添加適當的控件之後窗體樣式
Chart(繪圖)組件為定制自己的行為和外觀提供了很多選項
你可以使用向導(位於屬性窗體底部
如圖
所示)格式化這個Chart組件
圖
Chart組件的格式化向導-該向導為Chart組件提供了大量的格式化選項
使用示例的最簡單的方法是把下面一些Chart屬性復制並粘貼到Windows窗體設計器生成的代碼段中
Chart
Me
Chart
AxisX
Staggered = True
Me
Chart
AxisX
Step =
Me
Chart
AxisY
Step =
Me
Chart
BackObject = GradientBackground
Me
Chart
DataStyle =SoftwareFX
ChartFX
DataStyle
ReadXValues
Me
Chart
DesignTimeData = _
C:\Program Files\ChartFX for
NET
\Wizard\XYZero
txt
Me
Chart
Gallery = SoftwareFX
ChartFX
Gallery
Lines
Me
Chart
InsideColor = System
Drawing
Color
Transparent
Me
Chart
LineWidth =
Me
Chart
Location = New System
Drawing
Point(
)
Me
Chart
MarkerShape =SoftwareFX
ChartFX
MarkerShape
None
Me
Chart
Name =
Chart
Me
Chart
NSeries =
Me
Chart
NValues =
Me
Chart
Palette =
HighContrast
HighContrast
Me
Chart
PointLabels = True
Me
Chart
Size = New System
Drawing
Size(
)
Me
Chart
TabIndex =
Me
Chart
Titles
AddRange(New _
SoftwareFX
ChartFX
TitleDockable(){TitleDockable
})
同時
把下面一些數據項添加到組合框控件中
MSFT
SUN
YHOO
和
GE
你可以在Form_Load事件中進行這樣的操作
Me
cmbStocks
Items
AddRange(New String() {
MSFT
SUN
YHOO
GE
})
激活圖形 下一步
導入下面的名字空間(在代碼窗口的頂部)
Imports SoftwareFX
ChartFX
Imports System
Threading
定義用於線程的全局變量t
:
Dim t
As Thread
在Chart
_Load事件中
初始化Chart組件
Private Sub Chart
_Load(ByVal sender As _
System
Object
ByVal e As System
EventArgs) Handles Chart
Load
x軸上每隔
點顯示時間
Chart
AxisX
Step =
每個點之間用
象素間隔
Chart
AxisX
PixPerUnit =
使圖表可以滾動
Chart
Scrollable = True
打開和關閉通訊管道
Chart
OpenData(COD
Values
COD
Unknown)
Chart
CloseData(COD
Values)
End Sub
給當前的窗體添加一個叫做StockQuote的類
StockQuote類調用前面的Web服務並用返回的股票價格來更新圖表
Public Class StockQuote
組件中圖形的數量
Const NUM_SERIES =
Private lastPoint As Integer =
Dim stockPrice As Single
Private pStockSymbol As String
Private pStockSeries As Integer =
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
Service
For i As Integer =
To
stockPrice = ws
getPrice(pStockSymbol)
pChartControl
Invoke(New _
myDelegate(AddressOf updateChart)
New Object() {})
繼續之前等待
秒鐘
Thread
Sleep(
)
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 +=
pChartControl
CloseData(COD
Values)
把滾動條移到最右邊
pChartControl
AxisX
ScrollPosition = pChartControl
AxisX
ScrollSize
End Sub
End Class
你通過StockSymbol屬性把需要的股票編碼傳遞給StockQuote類並使用ChartControl屬性設置圖表更新InvokeWebService()方法在循環(示例中設置為)中周期性地調用上面的Web服務由於這個類會在一個單獨的線程中執行你必須非常小心以確保自己不會自動地更新某個Windows控件因為Windows控件並不是線程安全的(threadsafe)作為代替你必須使用委托並調用自己希望更新的控件上的Invoke()方法代碼每秒鐘調用Web服務一次這是由ThreadSleep()語句設置的
為了啟動線程用最新的股票信息更新圖表給獲取股票報價按鈕的點擊(Click)事件增加下面的代碼
Private Sub btnGetStockQuote
_Click(ByVal sender As System
Object
_
ByVal e As System
EventArgs) Handles btnGetStockQuote
Click
Dim sq As New StockQuote
sq
StockSymbol = cmbStocks
SelectedItem
sq
ChartControl = Chart
t
= New Thread(AddressOf sq
InvokeWebService)
t
Start()
End Sub
把調用該Web服務的代碼打包為一個類的主要原因是Thread類構造函數只能接受一個ThreadStart委托(啟動線程的方法的委托)
不存在可以接受多個參數值的重載的Thread
Start()方法
因此
把多個參數傳遞到一個線程中的唯一途徑是把調用的相關代碼打包為一個類
接著你就可以通過這個類的參數來傳遞參數
按F
測試這段代碼
選擇一只股票並點擊
獲取股票報價
按鈕
你現在可以移
動窗口了(即UI並沒有被重復的Web服務調用鎖死)
並且同時可以看到圖表一直在用最新的股票信息更新(圖
所示)
圖
測試該應用程序-當你選擇某只股票編碼並點擊
獲取股票報價
按鈕的時候
重復調用Web服務的結果顯示在圖表中
但是由於該Web服務運行在後台線程上
調用它不會影響正常的UI操作
顯示多只股票的價格
你已經看到了如何在保證應用程序的UI不停頓的情況下異步地調用Web服務了
但是
你還可以增強該應用程序來同時顯示多個信息
在同一個窗體中
增加另一組控件(ChartFX
組合框和按鈕)和標簽
暫停
停止按鈕(如圖
所示)
圖
增強的多股票窗體-此圖顯示了你需要添加到默認窗體上以同時顯示兩只股票圖形的新控件
這個增強的示例同時顯示了兩個圖形
還要顯示用於顯示第二個圖形的線程的狀態信息
添加第二個全局變量t
Dim t
t
As Thread
示例項目使用計時器控件(Timer
在工具箱中)來顯示第二個線程的狀態信息
把計時器拖放到窗體上
並把它的Interval屬性設置為
這使該計時器的Tick事件每半秒鐘(
毫秒)調用一次
Tick事件處理程序中的代碼更新了標簽控件lblThreadStatus中的線程狀態信息
Private Sub Timer
_Tick(ByVal sender As System
Object
_
ByVal e As System
EventArgs) Handles Timer
Tick
lblThreadStatus
Text =
Thread state:
& _
t
ThreadState
ToString
End Sub
第二個圖表也使用與第一個圖表相同的初始化代碼
Private Sub Chart
_Load(ByVal sender As System
Object
_
ByVal e As System
EventArgs) Handles Chart
Load
在x軸上每
點顯示時間
Chart
AxisX
Step =
每個點之間用
個象素分隔
Chart
AxisX
PixPerUnit =
使圖表可以滾動
Chart
Scrollable = True
打開和關閉通訊管道
Chart
OpenData(COD
Values
COD
Unknown)
Chart
CloseData(COD
Values)
End Sub
你點擊第二個圖表的獲取股票報價按鈕的時候代碼建立一個新的線程同時激活計時器這樣窗體才能夠顯示線程的狀態信息
Private Sub btnGetStockQuote
_Click(ByVal sender As System
Object
_
ByVal e As System
EventArgs) Handles btnGetStockQuote
Click
Dim sq As New StockQuote
sq
StockSymbol = cmbStocks
SelectedItem
sq
ChartControl = Chart
t
= New Thread(AddressOf sq
InvokeWebService)
t
Start()
激活暫停和停止按鈕
btnPauseContinue
Enabled = True
btnStop
Enabled = True
激活計時器控件
Timer
Enabled = True
End Sub
按F
測試這兩個圖表(圖
所示)
為每個圖表選擇一只股票
你將看到這兩個圖表同步顯示
圖
增強的兩圖表應用程序
增強的版本同時顯示了兩個圖表
當第二個線程運行的時候
你可以注意到其狀態在Running和WaitSleepJoin之間交替
這是因為某個線程要麼在執行(Running)
要麼在睡眠(WaitSleepJoin)
當該線程被暫停的時候
它的狀態是WaitSleepJoin
Suspended
當該線程被取消的時候
它的狀態先是AbortRequested
接著變成了Stopped
如果要暫停該線程
需要首先檢測運行中線程的狀態
然後使用Suspend()方法
在暫停一個線程之後
你可以使用Resume()方法繼續執行它
Private Sub btnPauseContinue_Click(ByVal sender As System
Object
_
ByVal e As System
EventArgs) Handles btnPauseContinue
Click
如果線程處於睡眠和運行狀態就掛起它
If t
ThreadState = ThreadState
WaitSleepJoin _
Or t
ThreadState = ThreadState
Running Then
t
Suspend()
btnPauseContinue
Text =
Continue
Else
繼續該線程
t
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 t
ThreadState = ThreadState
Stopped Then
btnPauseContinue
Enabled = False
btnStop
Enabled = False
t
Abort()
End If
Catch ex As Exception
MsgBox(ex
ToString)
End Try
End Sub
通過運行示例項目
你會發現自己已經能夠使用多線程技術建立應用程序
使應用程序在執行後台事務的時候
仍然保持響應
盡管本文的示例使用的是Web服務
但是相同的原則也可以應用於其它類型的後台事務
例如
你可以改變這個應用程序以讀取外部設備(例如溫度計或血壓計監視設備)的數據
From:http://tw.wingwit.com/Article/program/net/201311/11368.html