多執行緒程序的參數和傳回值 (C# 和 Visual Basic)
因為必須將執行緒類別的建構函式 (Constructor) 傳遞給不使用引數且不傳回值的程序參考,所以在多執行緒應用程式 (Multithreaded Application) 中提供和傳回值並不容易。 以下章節將說明一些簡單的方法,來提供參數以及從個別執行緒上的程序傳回值。
提供多執行緒程式的參數
為多執行緒方法呼叫提供參數的最佳做法是,將目標方法包裝在類別之中,接著為此類別定義將當做新執行緒參數的欄位。 這種做法的好處是,每次您想要啟動新執行緒時,就可以建立類別的新執行個體以及其本身的參數。 例如,假設您有個函式是用來計算三角形的面積,如下列程式碼所示:
Function CalcArea(ByVal Base As Double, ByVal Height As Double) As Double
CalcArea = 0.5 * Base * Height
End Function
double CalcArea(double Base, double Height)
{
return 0.5 * Base * Height;
}
您可以寫入類別來包裝 CalcArea 函式,並建立欄位以儲存輸入參數,如以下所示:
Class AreaClass
Public Base As Double
Public Height As Double
Public Area As Double
Sub CalcArea()
Area = 0.5 * Base * Height
MessageBox.Show("The area is: " & Area.ToString)
End Sub
End Class
class AreaClass
{
public double Base;
public double Height;
public double Area;
public void CalcArea()
{
Area = 0.5 * Base * Height;
MessageBox.Show("The area is: " + Area.ToString());
}
}
若要使用 AreaClass,您可以建立 AreaClass 物件,並設定 Base 和 Height 屬性,如下列程式碼所示:
Protected Sub TestArea()
Dim AreaObject As New AreaClass
Dim Thread As New System.Threading.Thread(
AddressOf AreaObject.CalcArea)
AreaObject.Base = 30
AreaObject.Height = 40
Thread.Start()
End Sub
protected void TestArea()
{
AreaClass AreaObject = new AreaClass();
System.Threading.Thread Thread =
new System.Threading.Thread(AreaObject.CalcArea);
AreaObject.Base = 30;
AreaObject.Height = 40;
Thread.Start();
}
請注意,TestArea 程序在呼叫 CalcArea 方法之後,不會檢查 Area 欄位的值。 由於 CalcArea 是在個別的執行緒上執行,因此如果您在呼叫 Thread.Start 之後立即檢查 Area 欄位,則不保證它已設定好。 下一章節將討論從多執行緒程序傳回值的較佳做法。
從多執行緒程序傳回值
從個別執行緒上執行的程序傳回值之所以複雜的原因是,程序不能是函式且無法使用 ByRef 引數。 傳回值的最簡單方式是使用 BackgroundWorker 元件管理執行緒,並在工作完成時引發事件,且使用事件處理常式處理結果。
以下範例將藉由引發事件來從個別執行緒上執行的程序傳回值:
Private Class AreaClass2
Public Base As Double
Public Height As Double
Function CalcArea() As Double
' Calculate the area of a triangle.
Return 0.5 * Base * Height
End Function
End Class
Private WithEvents BackgroundWorker1 As New System.ComponentModel.BackgroundWorker
Private Sub TestArea2()
Dim AreaObject2 As New AreaClass2
AreaObject2.Base = 30
AreaObject2.Height = 40
' Start the asynchronous operation.
BackgroundWorker1.RunWorkerAsync(AreaObject2)
End Sub
' This method runs on the background thread when it starts.
Private Sub BackgroundWorker1_DoWork(
ByVal sender As Object,
ByVal e As System.ComponentModel.DoWorkEventArgs
) Handles BackgroundWorker1.DoWork
Dim AreaObject2 As AreaClass2 = CType(e.Argument, AreaClass2)
' Return the value through the Result property.
e.Result = AreaObject2.CalcArea()
End Sub
' This method runs on the main thread when the background thread finishes.
Private Sub BackgroundWorker1_RunWorkerCompleted(
ByVal sender As Object,
ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs
) Handles BackgroundWorker1.RunWorkerCompleted
' Access the result through the Result property.
Dim Area As Double = CDbl(e.Result)
MessageBox.Show("The area is: " & Area.ToString)
End Sub
class AreaClass2
{
public double Base;
public double Height;
public double CalcArea()
{
// Calculate the area of a triangle.
return 0.5 * Base * Height;
}
}
private System.ComponentModel.BackgroundWorker BackgroundWorker1
= new System.ComponentModel.BackgroundWorker();
private void TestArea2()
{
InitializeBackgroundWorker();
AreaClass2 AreaObject2 = new AreaClass2();
AreaObject2.Base = 30;
AreaObject2.Height = 40;
// Start the asynchronous operation.
BackgroundWorker1.RunWorkerAsync(AreaObject2);
}
private void InitializeBackgroundWorker()
{
// Attach event handlers to the BackgroundWorker object.
BackgroundWorker1.DoWork +=
new System.ComponentModel.DoWorkEventHandler(BackgroundWorker1_DoWork);
BackgroundWorker1.RunWorkerCompleted +=
new System.ComponentModel.RunWorkerCompletedEventHandler(BackgroundWorker1_RunWorkerCompleted);
}
private void BackgroundWorker1_DoWork(
object sender,
System.ComponentModel.DoWorkEventArgs e)
{
AreaClass2 AreaObject2 = (AreaClass2)e.Argument;
// Return the value through the Result property.
e.Result = AreaObject2.CalcArea();
}
private void BackgroundWorker1_RunWorkerCompleted(
object sender,
System.ComponentModel.RunWorkerCompletedEventArgs e)
{
// Access the result through the Result property.
double Area = (double)e.Result;
MessageBox.Show("The area is: " + Area.ToString());
}
使用 QueueUserWorkItem 方法的選擇性 ByVal 狀態物件變數,即可提供參數並將值傳至執行緒集區執行緒。 執行緒計時器執行緒也支援狀態物件,以達成這個目的。 如需執行緒集區和執行緒計時器的詳細資訊,請參閱執行緒集區 (C# 和 Visual Basic)和執行緒計時器 (C# 和 Visual Basic)。
請參閱
工作
逐步解說:使用 BackgroundWorker 元件進行多執行緒處理 (C# 和 Visual Basic)