チュートリアル : Visual Basic による簡単なマルチスレッド コンポーネントの作成

BackgroundWorker コンポーネントは、System.Threading 名前空間に代わると共に追加の機能を提供します。ただし、System.Threading 名前空間は、下位互換性を保つ目的および将来使用する目的で保持されます。詳細については、「BackgroundWorker コンポーネントの概要」を参照してください。

複数のタスクを同時に実行できるアプリケーションを作成できます。この機能は、"マルチスレッド" または "フリー スレッド" と呼ばれ、プロセッサ集中型でユーザー入力の必要なコンポーネントをデザインするための強力な手段です。マルチスレッドを利用するコンポーネントの例としては、給与支払い情報を計算するコンポーネントがあります。プロセッサ集中型の給与計算を 1 つのスレッドで実行している間に、ユーザーがデータベースに入力したデータを別のスレッドで処理できます。プロセスを個別のスレッドで実行することによって、ユーザーはコンピューターの計算処理が完了するのを待たずに追加のデータを入力できます。このチュートリアルでは、複数の複雑な計算を同時に実行する簡単なマルチスレッド コンポーネントを作成します。

プロジェクトの作成

作成するアプリケーションは、1 つのフォームと 1 つのコンポーネントで構成されます。ユーザーが値を入力してコンポーネントに通知すると、計算が開始されます。フォームはコンポーネントから値を受け取り、その値をラベル コントロールに表示します。このコンポーネントはプロセッサ集中型の計算を実行し、完了時にフォームに通知します。ユーザー インターフェイスから受け取った値を保持するために、コンポーネントにパブリック変数を作成します。また、これらの変数の値に基づいて計算を実行するためのメソッドをコンポーネントに実装します。

[!メモ]

一般に、値を計算する方法としては関数が適していますが、スレッド間で引数を渡したり値を返したりできません。スレッドに値を渡したり、スレッドから値を受け取ったりするための簡単な方法は多数あります。この例では、パブリック変数を更新して、ユーザー インターフェイスに値を返します。また、スレッドが実行を完了したときに、イベントを使ってメイン プログラムに通知します。

実際に画面に表示されるダイアログ ボックスとメニュー コマンドは、アクティブな設定またはエディションによっては、ヘルプの説明と異なる場合があります。設定を変更するには、[ツール] メニューの [設定のインポートとエクスポート] をクリックします。詳細については、「Visual Studio の設定」を参照してください。

フォームを作成するには

  1. 新しい Windows アプリケーション プロジェクトを作成します。

  2. アプリケーションに Calculations という名前を付けて、Form1.vb の名前を frmCalculations.vb に変更します。

  3. Visual Studio によって、Form1 コード要素の名前を変更するように要求するメッセージが表示されたら、[はい] をクリックします。

    このフォームは作成するアプリケーションのプライマリ ユーザー インターフェイスとして機能します。

  4. 5 つの Label コントロール、4 つの Button コントロール、および 1 つの TextBox コントロールをフォームに追加します。

    Control

    名前

    テキスト

    Label1

    lblFactorial1

    (空白)

    Label2

    lblFactorial2

    (空白)

    Label3

    lblAddTwo

    (空白)

    Label4

    lblRunLoops

    (空白)

    Label5

    lblTotalCalculations

    (空白)

    Button1

    btnFactorial1

    Factorial

    Button2

    btnFactorial2

    Factorial - 1

    Button3

    btnAddTwo

    Add Two

    Button4

    btnRunLoops

    Run a Loop

    TextBox1

    txtValue

    (空白)

Calculator コンポーネントを作成するには

  1. [プロジェクト] メニューの [コンポーネントの追加] をクリックします。

  2. このコンポーネントに Calculator という名前を付けます。

Calculator コンポーネントにパブリック変数を追加するには

  1. コード エディターで Calculator を開きます。

  2. ステートメントを追加して、frmCalculations から各スレッドに値を渡すために使うパブリック変数を作成します。

    変数 varTotalCalculations はコンポーネントによって実行された現在までの計算処理の合計件数を保持し、その他の変数はフォームから値を受け取ります。

    Public varAddTwo As Integer
    Public varFact1 As Integer
    Public varFact2 As Integer
    Public varLoopValue As Integer
    Public varTotalCalculations As Double = 0
    

Calculator コンポーネントにメソッドとイベントを追加するには

  1. コンポーネントがフォームに値を渡すために使用するイベントを宣言します。前の手順で入力した変数宣言の直後に、次のコードを入力します。

    Public Event FactorialComplete(ByVal Factorial As Double, ByVal _
       TotalCalculations As Double)
    Public Event FactorialMinusComplete(ByVal Factorial As Double, ByVal _
       TotalCalculations As Double)
    Public Event AddTwoComplete(ByVal Result As Integer, ByVal _
       TotalCalculations As Double)
    Public Event LoopComplete(ByVal TotalCalculations As Double, ByVal _
       Counter As Integer)
    
  2. 手順 1. で入力した変数宣言の直後に、次のコードを入力します。

    ' This sub will calculate the value of a number minus 1 factorial 
    ' (varFact2-1!).
    Public Sub FactorialMinusOne()
       Dim varX As Integer = 1
       Dim varTotalAsOfNow As Double
       Dim varResult As Double = 1
       ' Performs a factorial calculation on varFact2 - 1.
       For varX = 1 to varFact2 - 1
          varResult *= varX
          ' Increments varTotalCalculations and keeps track of the current
          ' total as of this instant.
          varTotalCalculations += 1
          varTotalAsOfNow = varTotalCalculations
       Next varX
       ' Signals that the method has completed, and communicates the 
       ' result and a value of total calculations performed up to this 
       ' point
       RaiseEvent FactorialMinusComplete(varResult, varTotalAsOfNow)
    End Sub
    
    ' This sub will calculate the value of a number factorial (varFact1!).
    Public Sub Factorial()
       Dim varX As Integer = 1
       Dim varResult As Double = 1
       Dim varTotalAsOfNow As Double = 0
       For varX = 1 to varFact1
           varResult *= varX
           varTotalCalculations += 1
           varTotalAsOfNow = varTotalCalculations
       Next varX
       RaiseEvent FactorialComplete(varResult, varTotalAsOfNow)
    End Sub
    
    ' This sub will add two to a number (varAddTwo + 2).
    Public Sub AddTwo()
       Dim varResult As Integer
       Dim varTotalAsOfNow As Double
       varResult = varAddTwo + 2
       varTotalCalculations += 1
       varTotalAsOfNow = varTotalCalculations
       RaiseEvent AddTwoComplete(varResult, varTotalAsOfNow)
    End Sub
    
    ' This method will run a loop with a nested loop varLoopValue times.
    Public Sub RunALoop()
       Dim varX As Integer
       Dim varY As Integer
       Dim varTotalAsOfNow As Double
       For varX = 1 To varLoopValue
          ' This nested loop is added solely for the purpose of slowing
          ' down the program and creating a processor-intensive
          ' application.
          For varY = 1 To 500
             varTotalCalculations += 1
             varTotalAsOfNow = varTotalCalculations
          Next
       Next
       RaiseEvent LoopComplete(varTotalAsOfNow, varX - 1)
    End Sub
    

コンポーネントへのユーザー入力の転送

次の手順では、ユーザーからの入力を受け取ったり、Calculator コンポーネントと値をやり取りしたりするためのコードを frmCalculations に追加します。

frmCalculations にフロントエンド機能を実装するには

  1. [ビルド] メニューの [ソリューションのビルド] をクリックします。

  2. Windows フォーム デザイナーで frmCalculations を開きます。

  3. [ツールボックス][Calculations コンポーネント] タブを探します。Calculator コンポーネントをデザイン サーフェイス上にドラッグします。

  4. [プロパティ] ウィンドウ ツール バーの [イベント] をクリックします。

  5. 4 つの各イベントをダブルクリックして frmCalculations にイベント ハンドラーを作成します。各イベント ハンドラーが作成された後でデザイナーに戻る必要があります。

  6. 次のコードを挿入して、フォームが Calculator1 から受け取るイベントを処理します。

    Private Sub Calculator1_AddTwoComplete(ByVal Result As System.Int32, ByVal TotalCalculations As System.Double) Handles Calculator1.AddTwoComplete
        lblAddTwo.Text = Result.ToString
        btnAddTwo.Enabled = True
        lblTotalCalculations.Text = "TotalCalculations are " & _
            TotalCalculations.ToString
    End Sub
    
    Private Sub Calculator1_FactorialComplete(ByVal Factorial As System.Double, ByVal TotalCalculations As System.Double) Handles Calculator1.FactorialComplete
        ' Displays the returned value in the appropriate label.
        lblFactorial1.Text = Factorial.ToString
        ' Re-enables the button so it can be used again.
        btnFactorial1.Enabled = True
        ' Updates the label that displays the total calculations performed
        lblTotalCalculations.Text = "TotalCalculations are " & _
           TotalCalculations.ToString
    End Sub
    
    Private Sub Calculator1_FactorialMinusComplete(ByVal Factorial As System.Double, ByVal TotalCalculations As System.Double) Handles Calculator1.FactorialMinusComplete
        lblFactorial2.Text = Factorial.ToString
        btnFactorial2.Enabled = True
        lblTotalCalculations.Text = "TotalCalculations are " & _
            TotalCalculations.ToString
    End Sub
    
    Private Sub Calculator1_LoopComplete(ByVal TotalCalculations As System.Double, ByVal Counter As System.Int32) Handles Calculator1.LoopComplete
        btnRunLoops.Enabled = True
        lblRunLoops.Text = Counter.ToString
        lblTotalCalculations.Text = "TotalCalculations are " & _
           TotalCalculations.ToString
    End Sub
    
  7. コード エディターの下部にある End Class ステートメントを探します。このステートメントの直前に、ボタンのクリックを処理する次のコードを追加します。

    Private Sub btnFactorial1_Click(ByVal sender As Object, ByVal e As _
       System.EventArgs) Handles btnFactorial1.Click
       ' Passes the value typed in the txtValue to Calculator.varFact1.
       Calculator1.varFact1 = CInt(txtValue.Text)
       ' Disables the btnFactorial1 until this calculation is complete.
       btnFactorial1.Enabled = False
       Calculator1.Factorial()
    End Sub
    
    Private Sub btnFactorial2_Click(ByVal sender As Object, ByVal e _
       As System.EventArgs) Handles btnFactorial2.Click
       Calculator1.varFact2 = CInt(txtValue.Text)
       btnFactorial2.Enabled = False
       Calculator1.FactorialMinusOne()
    End Sub
    
    Private Sub btnAddTwo_Click(ByVal sender As Object, ByVal e As _
       System.EventArgs) Handles btnAddTwo.Click
       Calculator1.varAddTwo = CInt(txtValue.Text)
       btnAddTwo.Enabled = False
       Calculator1.AddTwo()
    End Sub
    
    Private Sub btnRunLoops_Click(ByVal sender As Object, ByVal e As _
       System.EventArgs) Handles btnRunLoops.Click
       Calculator1.varLoopValue = CInt(txtValue.Text)
       btnRunLoops.Enabled = False
       ' Lets the user know that a loop is running.
       lblRunLoops.Text = "Looping"
       Calculator1.RunALoop()
    End Sub
    

アプリケーションのテスト

これで、複数の複雑な計算を実行できるフォームとコンポーネントを取り込んだプロジェクトが作成されました。マルチスレッドの機能がまだ実装されていませんが、先に進む前にプロジェクトをテストして機能を検査します。

プロジェクトをテストするには

  1. [デバッグ] メニューの [デバッグ開始] をクリックします。アプリケーションが開始され frmCalculations が表示されます。

  2. テキスト ボックスに「4」と入力し、[Add two] というラベルの付いたボタンをクリックします。

    "6" という数字がこのラベルの下に表示され、lblTotalCalculations に "Total Calculations are 1" と表示されている必要があります。

  3. 次に [Factorial - 1] というラベルの付いたボタンをクリックします。

    "6" という数字がこのボタンの下に表示され、今度は lblTotalCalculations に "Total Calculations are 4" と表示されている必要があります。

  4. テキスト ボックスの値を「20」に変更し、[Factorial] というラベルの付いたボタンをクリックします。

    ボタンの下に "2.43290200817664E+18" という数字が表示され、今度は lblTotalCalculations に "Total Calculations are 24" と表示されます。

  5. テキスト ボックスの値を「50000」に変更し、[Run A Loop] というラベルの付いたボタンをクリックします。

    このボタンが再び使用できるようになるまでには、少し時間がかかります。このボタンの下のラベルに "50000" と表示され、合計計算数が "25000024" と表示されます。

  6. テキスト ボックスの値を「5000000」に変更し [Run A Loop] というラベルの付いたボタンをクリックし、すぐに [Add Two] というラベルの付いたボタンをクリックします。もう一度 [Add Two] をクリックします。

    ボタンおよびフォームのすべてのコントロールは、ループ処理が完了するまで応答しません。

    プログラムで実行されるスレッドが 1 つだけである場合は、上の例のようなプロセッサ集中型の計算を実行すると、計算が完了するまでプログラムが拘束される傾向があります。複数のスレッドが同時に実行できるように、次のセクションでは、マルチスレッド機能をアプリケーションに追加します。

マルチスレッド機能の追加

前の例ではシングル スレッドでだけ実行されるアプリケーションの制限について説明しました。次のセクションでは、Thread クラスを使って、複数の実行スレッドをコンポーネントに追加します。

Threads サブルーチンを追加するには

  1. コード エディターCalculator.vb を開きます。コードの先頭付近にある Public Class Calculator 行を探します。この行の直後に、次のコードを追加します。

    ' Declares the variables you will use to hold your thread objects.
    Public FactorialThread As System.Threading.Thread
    Public FactorialMinusOneThread As System.Threading.Thread
    Public AddTwoThread As System.Threading.Thread
    Public LoopThread As System.Threading.Thread
    
  2. このコード下部にある End Class ステートメントの直前に、次のメソッドを追加します。

    Public Sub ChooseThreads(ByVal threadNumber As Integer)
    ' Determines which thread to start based on the value it receives.
       Select Case threadNumber
          Case 1
             ' Sets the thread using the AddressOf the subroutine where
             ' the thread will start.
             FactorialThread = New System.Threading.Thread(AddressOf _
                Factorial)
             ' Starts the thread.
             FactorialThread.Start()
          Case 2
             FactorialMinusOneThread = New _
                System.Threading.Thread(AddressOf FactorialMinusOne)
             FactorialMinusOneThread.Start()
          Case 3
             AddTwoThread = New System.Threading.Thread(AddressOf AddTwo)
             AddTwoThread.Start()
          Case 4
             LoopThread = New System.Threading.Thread(AddressOf RunALoop)
             LoopThread.Start()
       End Select
    End Sub
    

    Thread オブジェクトをインスタンス化するときは、ThreadStart オブジェクトの形式の引数が必要です。ThreadStart オブジェクトは、スレッドを開始するサブルーチンのアドレスを指すデリゲートです。ThreadStart オブジェクトは、パラメーターを受け取ったり、値を渡したりできないため、関数を示すことはできません。AddressOf 演算子 (Visual Basic) は、ThreadStart オブジェクトとして機能するデリゲートを返します。実装した ChooseThreads サブルーチンが、呼び出し元のプログラムから値を受け取り、この値を使用することにより、開始する必要のある適切なスレッドを決定します。

frmCalculations にスレッドの起動コードを追加するには

  1. コード エディターfrmCalculations.vb ファイルを開きます。Sub btnFactorial1_Click を探します。

    1. 示すように Calculator1.Factorialの直接メソッドを呼び出す行をコメント アウトします:

      ' Calculator1.Factorial
      
    2. Calculator1.ChooseThreads メソッドを呼び出すため、次の行を追加します。

      ' Passes the value 1 to Calculator1, thus directing it to start the ' correct thread.
      Calculator1.ChooseThreads(1)
      
  2. 他の button_click サブルーチンにも同様の変更を加えます。

    [!メモ]

    threads 引数の適切な値が含まれていることを確認してください。

    完成したコードは次のようになります。

    Private Sub btnFactorial1_Click(ByVal sender As Object, ByVal e As _
       System.EventArgs) Handles btnFactorial1.Click
       ' Passes the value typed in the txtValue to Calculator.varFact1.
       Calculator1.varFact1 = CInt(txtValue.Text)
       ' Disables the btnFactorial1 until this calculation is complete.
       btnFactorial1.Enabled = False
       ' Calculator1.Factorial()
       ' Passes the value 1 to Calculator1, thus directing it to start the
       ' Correct thread.
       Calculator1.ChooseThreads(1)
    End Sub
    
    Private Sub btnFactorial2_Click(ByVal sender As Object, ByVal e As _
       System.EventArgs) Handles btnFactorial2.Click
       Calculator1.varFact2 = CInt(txtValue.Text)
       btnFactorial2.Enabled = False
       ' Calculator1.FactorialMinusOne()
       Calculator1.ChooseThreads(2)
    End Sub
    
    Private Sub btnAddTwo_Click(ByVal sender As Object, ByVal e As _
       System.EventArgs) Handles btnAddTwo.Click
       Calculator1.varAddTwo = CInt(txtValue.Text)
       btnAddTwo.Enabled = False
       ' Calculator1.AddTwo()
       Calculator1.ChooseThreads(3)
    End Sub
    
    Private Sub btnRunLoops_Click(ByVal sender As Object, ByVal e As _
       System.EventArgs) Handles btnRunLoops.Click
       Calculator1.varLoopValue = CInt(txtValue.Text)
       btnRunLoops.Enabled = False
       ' Lets the user know that a loop is running.
       lblRunLoops.Text = "Looping"
       ' Calculator1.RunALoop()
       Calculator1.ChooseThreads(4)
    End Sub
    

コントロールのマーシャリング呼び出し

ここでは、フォームの表示を円滑に更新できるようにします。コントロールは常にメインの実行スレッドによって所有されるため、従属スレッドからコントロールを呼び出すには "マーシャリング" 呼び出しが必要です。マーシャリングは、スレッド境界を越えて呼び出しを移動する方法で、リソースの観点から考えるとたいへんコストがかかります。マーシャリングの発生する回数を最小限にし、呼び出しをスレッド セーフな方法で行うには、BeginInvoke を使用して、メインの実行スレッドでメソッドを呼び出します。これにより、スレッドの境界を越えたマーシャリングの発生を必要最小限にできます。このような呼び出しは、コントロールを操作するメソッドを呼び出すときに必要です。詳細については、「方法 : スレッドからコントロールを操作する」を参照してください。

コントロールを呼び出すプロシージャを作成するには

  1. コード エディターで frmCalculations を開きます。宣言セクションに次のコードを追加します。

    Public Delegate Sub FHandler(ByVal Value As Double, ByVal _
       Calculations As Double)
    Public Delegate Sub A2Handler(ByVal Value As Integer, ByVal _
       Calculations As Double)
    Public Delegate Sub LDhandler(ByVal Calculations As Double, ByVal _
       Count As Integer)
    

    InvokeBeginInvoke では、適切なメソッドへのデリゲートが引数として必要です。これらの行では、適切なメソッドを呼び出すために BeginInvoke によって使用されるデリゲートのシグネチャを宣言します。

  2. コードに次の空のメソッドを追加します。

    Public Sub FactHandler(ByVal Factorial As Double, ByVal TotalCalculations As _
       Double)
    End Sub
    Public Sub Fact1Handler(ByVal Factorial As Double, ByVal TotalCalculations As _
       Double)
    End Sub
    Public Sub Add2Handler(ByVal Result As Integer, ByVal TotalCalculations As _
       Double)
    End Sub
    Public Sub LDoneHandler(ByVal TotalCalculations As Double, ByVal Counter As _
       Integer)
    End Sub
    
  3. [編集] メニューの [切り取り][貼り付け] を使って、Sub Calculator1_FactorialComplete のすべてのコードを切り取り、FactHandler に貼り付けます。

  4. Calculator1_FactorialMinusComplete と Fact1Handler、Calculator1_AddTwoComplete と Add2Handler、および Calculator1_LoopComplete と LDoneHandler について前の手順を繰り返します。

    終了すると、Calculator1_FactorialComplete、Calculator1_FactorialMinusComplete、Calculator1_AddTwoComplete、および Calculator1_LoopComplete に残されたコードはなく、これらに含まれていたすべてのコードは適切な新しいメソッドに移動されています。

  5. BeginInvoke メソッドを呼び出して、メソッドを非同期的に呼び出します。BeginInvoke は、フォーム (me) またはフォーム上の任意のコントロールから呼び出すことができます。

    Private Sub Calculator1_FactorialComplete(ByVal Factorial As System.Double, ByVal TotalCalculations As System.Double) Handles Calculator1.FactorialComplete
       ' BeginInvoke causes asynchronous execution to begin at the address
       ' specified by the delegate. Simply put, it transfers execution of 
       ' this method back to the main thread. Any parameters required by 
       ' the method contained at the delegate are wrapped in an object and 
       ' passed. 
       Me.BeginInvoke(New FHandler(AddressOf FactHandler), New Object() _
          {Factorial, TotalCalculations })
    End Sub
    
    Private Sub Calculator1_FactorialMinusComplete(ByVal Factorial As System.Double, ByVal TotalCalculations As System.Double) Handles Calculator1.FactorialMinusComplete
       Me.BeginInvoke(New FHandler(AddressOf Fact1Handler), New Object() _
          { Factorial, TotalCalculations })
    End Sub
    
    Private Sub Calculator1_AddTwoComplete(ByVal Result As System.Int32, ByVal TotalCalculations As System.Double) Handles Calculator1.AddTwoComplete
       Me.BeginInvoke(New A2Handler(AddressOf Add2Handler), New Object() _
          { Result, TotalCalculations })
    End Sub
    
    Private Sub Calculator1_LoopComplete(ByVal TotalCalculations As System.Double, ByVal Counter As System.Int32) Handles Calculator1.LoopComplete
       Me.BeginInvoke(New LDHandler(AddressOf Ldonehandler), New Object() _
          { TotalCalculations, Counter })
    End Sub
    

    一見すると、イベント ハンドラーは単純に次のメソッドを呼び出しているように見えます。実際には、イベント ハンドラーは処理のメイン スレッド上のメソッドを呼び出しています。この方法によりスレッドの境界を越えた呼び出しを減らし、ロックアップの発生を抑えながらマルチスレッド アプリケーションを効率的に実行できます。マルチスレッド環境でコントロールを使用する方法の詳細については、「方法 : スレッドからコントロールを操作する」を参照してください。

  6. 作業内容を保存します。

  7. [デバッグ] メニューの [デバッグ開始] をクリックしてソリューションをテストします。

    1. テキスト ボックスに「10000000」と入力し [Run A Loop] をクリックします。

      このボタンの下のラベルに "Looping" と表示されます。このループの実行にはかなり時間がかかります。完了が早すぎる場合は、数字の大きさを適宜変更してください。

    2. まだ選択可能な 3 つのボタンを連続してクリックします。すべてのボタンが入力に応答することがわかります。[Add Two] の下のラベルが最初に結果を表示します。その後、[factorial] ボタンの下のラベルに結果が表示されます。10,000,000 の階乗から返される数値は大きすぎて倍精度の変数には入りきらないため、これらの結果は無限になります。最後に少し遅れて、結果が [Run A Loop] ボタンの下に表示されます。

      以上の結果から、4 つの異なる計算のセットが 4 つの異なるスレッドで同時に実行されました。ユーザー インターフェイスは入力に応答し続け、各スレッドの完了後に結果が返されました。

スレッドの調整

マルチスレッド アプリケーションを扱い慣れている方なら、入力したコードに不備があることにお気付きかもしれません。Calculator の計算を実行する各サブルーチンのコード行を思い出してください。

varTotalCalculations += 1
varTotalAsOfNow = varTotalCalculations

これらの 2 行のコードでは、パブリック変数 varTotalCalculations をインクリメントし、ローカル変数 varTotalAsOfNow をこの値に設定します。次に、この値は frmCalculations に返され、ラベル コントロールに表示されます。ところで、返された値は正しいでしょうか。シングル スレッドの実行によって動作している場合に限っては、確実に正しい値が返されます。しかし、複数のスレッドが実行されている場合は、正しい値が返されることは保証されません。各スレッドは、変数 varTotalCalculations をインクリメントできます。あるスレッドによってこの変数がインクリメントされた後、インクリメントされた値が varTotalAsOfNow にコピーされる前に、別のスレッドによってインクリメントされ、変数の値が変更されてしまう可能性があります。つまり、実際には、各スレッドが正しくない結果をレポートしている可能性があります。Visual Basic には、スレッドの同期を可能にし、各スレッドが常に正確な結果を返すようにするための SyncLock ステートメント が用意されています。SyncLock の構文は次のとおりです。

SyncLock AnObject
   Insert code that affects the object
   Insert some more
   Insert even more
' Release the lock
End SyncLock

SyncLock ブロックが入力されると、指定された式の実行は、指定されたスレッドが対象のオブジェクトを排他ロックするまでブロックされます。上記の例では、実行は AnObject に対してブロックされます。SyncLock は、値ではなく参照を返すオブジェクトで使用します。このようにして、実行が他のスレッドによって干渉されることなく 1 つのブロックとして続行されます。1 つの単位として実行されるステートメントのセットは、分割不可能であることになります。End SyncLock を検出すると、式が解放され、スレッドは通常どおりに実行されます。

アプリケーションに SyncLock ステートメントを追加するには

  1. コード エディターCalculator.vb を開きます。

  2. 次のコードが記述されたすべての場所を探します。

    varTotalCalculations += 1
    varTotalAsOfNow = varTotalCalculations
    

    計算メソッドごとに 1 つずつ、このコードの 4 つのインスタンスが必要です。

  3. コードを次のように変更します。

    SyncLock Me
       varTotalCalculations += 1
       varTotalAsOfNow = varTotalCalculations
    End SyncLock
    
  4. 作業内容を保存し、前の例と同様にテストします。

    プログラムのパフォーマンスにわずかな影響があることがわかります。これはコンポーネントに排他ロックがかけられるときにスレッドの実行が停止するために起こります。この方法によって、正確さは保証されますが、複数のスレッドによって得られるパフォーマンス上の利点が若干損なわれます。スレッドのロックは、必要かどうかを慎重に検討して、不可欠な場合にだけ実装してください。

参照

処理手順

方法 : 複数の実行スレッドを調整する

チュートリアル : Visual C# による簡単なマルチスレッド コンポーネントの作成

関連項目

BackgroundWorker

概念

イベントベースの非同期パターンの概要

その他の技術情報

コンポーネントによるプログラミング

コンポーネント プログラミングのチュートリアル

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