Parametri e valori restituiti per routine multithreading (C# e Visual Basic)

L'invio e la restituzione di valori in un'applicazione multithreading è un processo complesso, perché è necessario che il costruttore della classe di thread riceva un riferimento a una routine che non riceve alcun argomento e non restituisce alcun valore. Nelle sezioni seguenti sono indicati alcuni semplici metodi per l'inserimento di parametri e la restituzione di valori da routine su thread diversi.

Inserimento di parametri per routine multithreading

Il modo migliore per inserire i parametri per una chiamata di metodo multithreading consiste nel racchiudere il metodo desiderato in una classe e definire per tale classe dei campi che serviranno da parametri per il nuovo thread. Il vantaggio di questo approccio è dato dalla possibilità di creare una nuova istanza della classe, con parametri propri, ogni volta che si desidera iniziare un nuovo thread. Si supponga, ad esempio, di avere una funzione che calcola l'area di un triangolo, come nel codice che segue:

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

È possibile scrivere una classe che racchiuda la funzione CalcArea e crei i campi in cui memorizzare i parametri di input, come nell'esempio seguente:

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

Per utilizzare AreaClass, è possibile creare un oggetto AreaClass e impostare le proprietà Base e Height come mostrato nel codice che segue:

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

Si noti che la routine TestArea non verifica il valore del campo Area dopo che è stato chiamato il metodo CalcArea. Dato che CalcArea viene eseguito su un thread separato, il campo Area non sarà necessariamente impostato se lo si controlla subito dopo avere chiamato Thread.Start. Nella sezione che segue è illustrato un metodo più efficace per la restituzione di valori dalle routine multithreading.

Restituzione di valori da routine multithreading

La restituzione di valori da routine eseguite su thread diversi è complicata dal fatto che le routine non possono essere funzioni e non possono utilizzare argomenti ByRef. Il modo più semplice per eseguire la restituizione di valori consiste nell'utilizzare il componente BackgroundWorker per gestire i thread e generare un evento una volta completata l'attività, quindi elaborare i risultati tramite un gestore eventi.

L'esempio seguente restituisce un valore generando un evento da una routine eseguita su un thread separato:

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

La variabile facoltativa dell'oggetto di stato ByVal del metodo QueueUserWorkItem consente di fornire parametri e restituire valori ai thread del pool di thread. Un oggetto di stato viene supportato a questo scopo anche dai thread dei timer di thread. Per informazioni sul pooling dei thread e i timer di thread, vedere Creazione di pool di thread (C# e Visual Basic) e Timer di thread (C# e Visual Basic).

Vedere anche

Attività

Procedura dettagliata: multithreading con il componente BackgroundWorker (C# e Visual Basic)

Riferimenti

Sincronizzazione di thread (C# e Visual Basic)

Eventi (Guida per programmatori C#)

Delegati (Guida per programmatori C#)

Concetti

Creazione di pool di thread (C# e Visual Basic)

Applicazioni multithreading (C# e Visual Basic)

Altre risorse

Eventi (Visual Basic)

Delegati (Visual Basic)

Multithreading nei componenti