Freigeben über


Parameter und Rückgabewerte für Multithreadprozeduren (C# und Visual Basic)

Die Bereitstellung und Rückgabe von Werten ist in einer Multithreadanwendung relativ kompliziert, da dem Konstruktor für die Threadklasse ein Verweis auf eine Prozedur übergeben werden muss, die keine Argumente akzeptiert und keinen Wert zurückgibt. In den folgenden Abschnitten wird beschrieben, wie auf einfache Weise Parameter bereitgestellt und Werte aus Prozeduren in separaten Threads zurückgegeben werden können.

Bereitstellen von Parametern für Multithreadprozeduren

Die einfachste Methode, Parameter für den Aufruf einer Multithreadmethode bereitzustellen, besteht darin, die Zielmethode mit einer Klasse zu umschließen und für diese Klasse Felder zu definieren, die als Parameter für den neuen Thread verwendet werden. Dieser Ansatz hat den Vorteil, dass Sie jedes Mal, wenn ein neuer Thread gestartet werden soll, eine neue Instanz der Klasse mit eigenen Parametern erstellen können. Angenommen, Sie verwenden eine Funktion, die die Fläche eines Dreiecks berechnet, wie in folgendem Codebeispiel:

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;
}

Sie können wie folgt eine Klasse programmieren, die die CalcArea-Funktion umschließt und Felder zum Speichern von Eingabeparametern erstellt:

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());
    }
}

Wenn Sie AreaClass verwenden möchten, können Sie ein AreaClass-Objekt erstellen und die Eigenschaften Base und Height festlegen, wie in folgendem Codebeispiel gezeigt:

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();
}

Beachten Sie, dass die TestArea-Prozedur nach dem Aufrufen der CalcArea-Methode nicht den Wert des Area-Feldes überprüft. Da CalcArea in einem separaten Thread ausgeführt wird, ist es nicht sicher, dass das Area-Feld festgelegt ist, wenn es unmittelbar nach dem Aufrufen von Thread.Start überprüft wird. Im nächsten Abschnitt wird eine bessere Methode für die Rückgabe von Werten aus Multithreadprozeduren beschrieben.

Rückgabe von Werten aus Multithreadprozeduren

Die Rückgabe von Werten aus Prozeduren, die in separaten Threads ausgeführt werden, gestaltet sich etwas kompliziert. Grund hierfür ist, dass Prozeduren keine Funktionen sein können und keine ByRef-Argumente verwenden dürfen. Die einfachste Möglichkeit zum Zurückgeben von Werten ist die Verwendung der BackgroundWorker-Komponente, um die Threads zu verwalten und nach Abschluss der Aufgabe ein Ereignis auszulösen, und die Ergebnisse mit einem Ereignishandler zu verarbeiten.

Im folgenden Beispiel wird ein Wert zurückgegeben, indem ein Ereignis aus einer Prozedur ausgelöst wird, die in einem separaten Thread ausgeführt wird:

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());
}

Sie können Parameter und Rückgabewerte für Threadpool-Threads bereitstellen, indem Sie die optionale ByVal-Zustandsobjektvariable der QueueUserWorkItem-Methode verwenden. Threadzeitgeber-Threads unterstützen ebenfalls ein Zustandsobjekt für diesen Zweck. Informationen über Threadpooling und Threadzeitgeber finden Sie unter Threadpooling (C# und Visual Basic) und unter Threadzeitgeber (C# und Visual Basic)

Siehe auch

Aufgaben

Exemplarische Vorgehensweise: Multithreading mit der BackgroundWorker-Komponente (C# und Visual Basic)

Referenz

Threadsynchronisierung (C# und Visual Basic)

Ereignisse (C#-Programmierhandbuch)

Delegaten (C#-Programmierhandbuch)

Konzepte

Threadpooling (C# und Visual Basic)

Multithreadanwendungen (C# und Visual Basic)

Weitere Ressourcen

Ereignisse (Visual Basic)

Delegaten (Visual Basic)

Multithreading in Komponenten