在你的VB.NET應用程式中使用多執行緒 (轉)

gugu99發表於2008-05-13
在你的VB.NET應用程式中使用多執行緒 (轉)[@more@]

在你的應用中使用多執行緒

  很長時間以來,開發人員一直要求為VB增加更多的執行緒功能——這一點在VB.NET中終於實現了。不支援建立多執行緒的EXE、DLL以及OCX。但這種措詞容易引起誤解,這是因為VB6支援多個單執行緒的單元。一個單元實際上是程式碼執行的場所而且單元的邊界限制了外部程式碼對單元內部的訪問。

  VB.NET支援建立自由執行緒的應用程式。這意味著多個執行緒可以訪問同一個共享的資料集。本文將帶領你瞭解多執行緒的基本內容。:namespace prefix = o ns = "urn:schemas--com::office" />

雖然VB支援多個單執行緒的單元,但並不支援允許多個執行緒在同一個資料集上執行的自由執行緒模型。在很多情況下,產生一個執行後臺處理程式的新執行緒會提高應用程式的可用性。一種很顯然的情況就是當執行一個可能使窗體看起來停止響應的長過程時,你一定會想在窗體上放置一個取消按鈕。

解決方法

由於VB.NET使用公共語言執行時(Common Language Runtime),它增強了很多新的特性,其中之一便是建立自由執行緒應用程式的能力。

在VB.NET中,開始使利用執行緒進行工作是很容易的。稍後我們會探究一些精妙之處,我們先建立一個簡單的窗體,它生成一個執行後臺處理程式的新執行緒。我們需要做的第一件事是將要在新執行緒上執行的後臺處理程式。下面的程式碼執行一個相當長的執行過程——一個無限迴圈:

Private Sub BackgroundProcess()

  Dim i As Integer = 1

  Do While True

  ListBox1.Items.Add("Iterations: " + i)

  i += 1

  L

End Sub

這段程式碼無限地迴圈並在每次迴圈中向窗體上的列表框中增加一個條目。如果你對VB.NET不熟悉的話,便會發現這段程式碼中有一些在VB6中無法完成的事:

l  在宣告變數時對其賦值 Dim i As Integer=1

l  使用+=運算子 i+=1代替了i=i+1

l  Call關鍵字已經被去除了

一旦我們有了一個工作過程,便需要將這段程式碼指派給一個新的執行緒並開始它的執行。完成這項工作,我們需要使用Thread,它是.NET類中System.Threading名稱空間的一部分。當例項化了一個新的Thread類時,我們向其傳遞一個引用,這個引用指向我們想要在Thread類的構造中執行的程式碼塊。下面的程式碼建立一個新的Thread物件並將指向BackgroundProcess的引用傳遞給它:

Dim t As Thread

t = New Thread(AddressOf Me.BackgroundProcess)

t.Start()

AddressOf運算子為BackgroundProcess方法建立了一個委派物件。委派在VB.NET中是一種型別的、物件導向的函式指標。線上程被例項化之後,你可以透過執行緒的Start()方法開始執行程式碼。

使執行緒處於控制之下

當執行緒開始之後,你可以透過使用Thread物件的方法對其狀態進行一定的控制。你可以透過呼叫Thread.Sleep方法暫停執行緒的執行。這個方法接收一個表示執行緒將要休眠多長時間的整型數值。如果在上例中你想要減緩列表框條目的新增,在程式碼中放置一個對此方法的呼叫:

Private Sub BackgroundProcess()

  Dim i As Integer = 1

  Do While True

  ListBox1.Items.Add("Iterations: " + i)

  i += 1

  Thread.CurrentThread.Sleep(2000)

  Loop

End Sub

CurrentThread是一個公共靜態屬性,它可以使你獲取一個對當前執行執行緒的引用。

你還可以透過呼叫Thread.Sleep (System.Threading.Timeout.Infinite)使一個執行緒處於一種時間不確定的休眠狀態。要中斷這種休眠,可以呼叫Thread.Interrupt 方法。

類似與Sleep和Interrupt的是Suspend和Resume。Suspend允許你阻塞一個執行緒直到另外的執行緒呼叫Thread.Resume。Sleep和Suspend之間的區別在於後者不是立即使一個執行緒處於等待狀態。在.NET執行時確定執行緒是處於一個安全的掛起位置之前,執行緒是不會掛起的。Sleep則是立即使執行緒進入等待狀態。

最後,Thread.Abort中止一個執行緒的執行。在我們的簡單例子中,我們還想增加另外一個可以使我們中止程式的按鈕。要完成這些,我們所需做的一切便是如下面這樣呼叫Thread.Abort方法:

Private Sub Button2_Click(ByVal sender As System., _

  ByVal e As System.EventArgs) Handles Button2.Click

  t.Abort()

End Sub

在此便可以看出多執行緒的能力。介面看起來對使用者是有響應的,因為它執行在一個執行緒中而後臺的處理程式執行在另一個執行緒中。取消按鈕會立即響應使用者的click事件同時處理過程被中止。

透過多執行緒的過程傳遞資料

上一個例子展示了一種相當簡單的情況。在你的時候,多執行緒有很多需要解決的複雜問題。你將會遇到的一個問題是向傳遞給Thread類建構函式的過程傳遞資料以及從這個過程傳出資料。換言之,你想要在另一個執行緒上開始的過程不能接收任何引數而且你也不能從這個過程返回任何資料。這是因為傳遞給執行緒建構函式的過程不能有任何引數或返回值。為了避開這個問題,將你的過程包裝到一個類中,在這個類中此方法的引數被表示成類的一個域。

有一個簡單的例子,如果我們有一個計算一個數的平方的過程:

Function Square(ByVal Value As Double) As Double

  Return Value * Value

End Function

為了使這個過程可以在一個新執行緒中使用,我們將其包裝到一個類中:

Public Class SquareClass

  Public Value As Double

  Public Square As Double

  Public Sub CalcSquare()

  Square = Value * Value

  End Sub

End Class

使用這些程式碼在一個新執行緒中啟動CalcSquare過程,程式碼如下:

Private Sub Button1_Click(ByVal sender As System.Object, _

  ByVal e As System.EventArgs) Handles Button1.Click

  Dim oSquare As New SquareClass()

  t = New Thread(AddressOf oSquare.CalcSquare)

  oSquare.Value = 30

  t.Start()

End Sub

注意當執行緒開始後,我們沒有檢查類的平方值,因為並不能保證一旦你呼叫執行緒Start方法,它便會執行。有一些方法可以從另外的執行緒中獲取這個值。最簡單的方法是當執行緒完成時引發一個事件。我們會在下一個部分執行緒同步中討論另外一種方法。下面的程式碼為SquareClass增加了事件宣告。

Public Class SquareClass

  Public Value As Double

  Public Square As Double

  Public Event ThreadComplete(ByVal Square As Double)

  Public Sub CalcSquare()

  Square = Value * Value

  RaiseEvent ThreadComplete(Square)

  End Sub

End Class

在呼叫程式碼中捕獲這個事件與VB6相比沒有太大的變化,仍然是用WithEvents宣告變數並在一個過程中處理事件。變化的部分是用Handles關鍵字宣告處理事件的過程並且不再使用像VB6中Object_Event的命名約定。

Dim WithEvents oSquare As SquareClass

 

Private Sub Button1_Click(ByVal sender As System.Object, _

  ByVal e As System.EventArgs) Handles Button1.Click

  oSquare = New SquareClass()

  t = New Thread(AddressOf oSquare.CalcSquare)

  oSquare.Value = 30

  t.Start()

End Sub

 

Sub SquareEventHandler(ByVal Square As Double) _

  Handles oSquare.ThreadComplete

  MsgBox("The square is " & Square)

End Sub

這個方法需要注意的一個問題是處理事件的過程,在本例中是SquareEventHandler,將執行在引發事件的執行緒中,而不是執行在窗體從中執行的執行緒中。

執行緒同步

VB.NET包含了一些語句用於提供執行緒的同步。在Square的例子中,你可能想同步執行計算的執行緒以便等到計算完成,這樣便可以獲得結果。舉另外一個例子,如果你在一個單獨的執行緒中對陣列進行排序並且在使用這個陣列之前要等待這個處理過程結束。為了實現這些同步,VB.NET提供了SyncLock語句和Thread.Join方法。

SyncLock獲取了對傳遞給它的物件引用的獨佔性鎖。透過取得這種獨佔鎖,你可以確保多個執行緒不會訪問共享的資料或是在多個執行緒上執行程式碼。一個可以方便地用於獲取鎖地物件是關聯於每個類的System.Type物件。可以透過GetType方法獲得System.Type物件:

Public Sub CalcSquare()

  SyncLock GetType(SquareClass)

  Square = Value * Value

  End SyncLock

End Sub

最後,Thread.Join方法允許你等待一段特定的時間直到一個執行緒結束。如果執行緒在你所確定的時間之前完成,Thread.Join返回True,否則的話返回False。在Square的例子中,如果我們不想引發事件,可以呼叫Thread.Join方法來確定計算是否已經結束。程式碼如下所示:

Private Sub Button1_Click(ByVal sender As System.Object, _

  ByVal e As System.EventArgs) Handles Button1.Click

  Dim oSquare As New SquareClass()

   t = New Thread(AddressOf oSquare.CalcSquare)

  oSquare.Value = 30

  t.Start()

  If t.Join(500) Then

  MsgBox(oSquare.Square)

  End If

End Sub

 

作者:Matthew Arnheiter是位於哈肯薩克市(ensack)的GoAmerica Communications公司的一名資深顧問,他還是The Developer's Gu to Design Patterns and UML (Sybex, 2000)一書的作者。可以透過(201)996-1717 x2367或Marnheiter◎goamerica.net與他取得聯絡。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10748419/viewspace-1003960/,如需轉載,請註明出處,否則將追究法律責任。

相關文章