次の方法で共有


マルチスレッド プロシージャのパラメーターと戻り値 (C# および Visual Basic)

スレッド クラスのコンストラクターは、引数を取らず値を返さないプロシージャに参照を渡す必要があるため、マルチスレッド アプリケーションでの値の受け渡しは複雑です。以下のセクションでは、異なるスレッドのプロシージャからパラメーターを指定して値を返す単純な方法を示します。

マルチスレッド プロシージャのパラメーターの指定

マルチスレッド メソッドの呼び出しにパラメーターを指定する最良の方法は、クラスにターゲット メソッドをラップし、新規スレッドのパラメーターとして機能するフィールドをそのクラスに対して定義することです。この方法の利点は、新規スレッドを開始するたびに独自のパラメーターでクラスの新規インスタンスを作成できることです。たとえば、次のコードに示すように三角形の面積を計算する関数があるとします。

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)

関連項目

スレッドの同期 (C# および Visual Basic)

イベント (C# プログラミング ガイド)

デリゲート (C# プログラミング ガイド)

概念

スレッド プール (C# および Visual Basic)

マルチスレッド アプリケーション (C# および Visual Basic)

その他の技術情報

イベント (Visual Basic)

デリゲート (Visual Basic)

コンポーネントのマルチスレッド