SSブログ

VB.NET で非同期デリゲートを使ったマルチスレッドの実装方法 [プログラミング]

VB.NETでマルチスレットを実装する方法はいくつかあるけれど、自分は「非同期デリゲート」を利用したマルチスレッドを利用している。

色々検討した結果、この方法が実行したスレッドの処理結果を把握しやすいので…

以下の例では"Thread1"と"Thread2"の二つの非同期処理を実行している。
"Thread1"は非同期処理が引数を必要としない場合で"Thread2"は非同期処理が引数を必要とする場合。

各非同期処理の戻り値は"THREAD_RETURN"構造体とすることで、処理の開始時刻、終了時刻、処理結果を取得可能にしている。


処理の流れは"Thread1"、"Thread2"共にほぼ同様で、大まかな流れは以下の通り。

1."fncThread[n]Start"プロシージャで非同期処理のデリゲートインスタンスを生成。
 生成したインスタンスのBeginInvokeメソッドを呼び出し"fncThread[n]"を起動する。その際、コールバック関数として"subThread[n]Callback"を指定し、非同期処理終了時にコールバック関数で終了処理を行う。
 ※"Thread2"ではコールバック関数にも引数を渡している。(例では引数を取得するのみで利用していないが…)

2."fncThread[n]"プロシージャに非同期処理として実行したい処理を記述する。

3."subThread[n]Callback"プロシージャで非同期処理の終了処理を行う。
 このプロシージャは"fncThread[n]"が終了すると呼び出されるが、非同期処理が完了しているかをデリゲートインスタンスのBeginInvokeメソッド呼び出し時に指定した戻り値"gobjThread[n]Return"のIsCompletedプロパティで確認する。もし完了していない場合は完了するまで待機する。
 非同期処理の完了が確認できたらデリゲートインスタンスのEndInvokeメソッドを呼び出して非同期処理の戻り値を取得する。
 そして、非同期処理終了時に実行したい処理を行った後、デリゲートインスタンスを破棄する。


以下の例を実行するには、新規の「コンソールアプリケーション」を作成し、コードを"Module1"に貼り付けて実行すれば良い。
コンソールウインドウが開き、各非同期処理が出力するメッセージが表示される。各非同期処理が終了するとメッセージボックスに各々の処理時間が出力される。

この例は非同期処理でマルチスレッドを実現する単純な処理だが、「Windowsフォームアプリケーション」で非同期処理を実行してその処理からフォームの内容を更新する場合にはスレッドセーフを意識したコードの記述が必要になる。
その方法についてはまた今度…。

------------------------------------------------------------

Module Module1

    '非同期処理のデリゲート宣言
    Delegate Function Thread1Delegate() As THREAD_RETURN
    Delegate Function Thread2Delegate( _
              ByVal pParam As Integer, _
              ByVal pParam2 As Integer) As THREAD_RETURN

    '型宣言
    'スレッド戻り値
    Public Structure THREAD_RETURN
        Dim dStart As Date
        Dim dEnd As Date
        Dim blnState As Boolean
    End Structure

    'ユーザ定義変数
    '非同期処理デリゲートインスタンス
    Public gobjThread1Delegate As Thread1Delegate
    Public gobjThread2Delegate As Thread2Delegate

    '非同期処理戻り値
    Public gobjThread1Return, gobjThread2Return _
              As IAsyncResult
    Public gtypReturn1, gtypReturn2 As THREAD_RETURN

    '非同期処理実行中判定変数
    Public gblnThread1Exec, gblnThread2Exec As Boolean

    Sub Main()

        If fncThread1Start() = False Then
            MsgBox("非同期処理①の起動に失敗しました。", _
                   MsgBoxStyle.Exclamation)
        End If

        If fncThread2Start() = False Then
            MsgBox("非同期処理②の起動に失敗しました。", _
                   MsgBoxStyle.Exclamation)
        End If

        Do While gblnThread1Exec = True OrElse _
                 gblnThread2Exec = True
            System.Threading.Thread.Sleep(1000)
        Loop

        Dim oSpan As TimeSpan
        Dim strMsg As String = _
                "すべての非同期処理が終了しました。" & _
                ControlChars.CrLf
        With gtypReturn1
            If .blnState = True Then
                '非同期処理①が正常終了の場合
                '処理時間を算出
                oSpan = .dEnd - .dStart
                'メッセージを編集
                strMsg = strMsg & "Thread1処理時間:" & _
                    oSpan.Hours.ToString("00") & ":" & _
                    oSpan.Minutes.ToString("00") & ":" & _
                    oSpan.Seconds.ToString("00") & "." & _
                    oSpan.Milliseconds.ToString("000") & _
                    ControlChars.CrLf
            Else
                '非同期処理①が異常終了の場合
                'メッセージを編集
                strMsg = strMsg & _
                    "Thread1:異常終了" & ControlChars.CrLf
            End If
        End With
        With gtypReturn2
            If .blnState = True Then
                '非同期処理②が正常終了の場合
                '処理時間を算出
                oSpan = .dEnd - .dStart
                'メッセージを編集
                strMsg = strMsg & "Thread2処理時間:" & _
                    oSpan.Hours.ToString("00") & ":" & _
                    oSpan.Minutes.ToString("00") & ":" & _
                    oSpan.Seconds.ToString("00") & "." & _
                    oSpan.Milliseconds.ToString("000")
            Else
                '非同期処理②が異常終了の場合
                'メッセージを編集
                strMsg = strMsg & "Thread2:異常終了"
            End If
        End With
        MsgBox(strMsg, MsgBoxStyle.Information)

    End Sub

    Public Function fncThread1Start() As Boolean
        '*************************
        '非同期処理①開始処理
        '*************************

        Dim blnReturn As Boolean

        Try
            '非同期処理①実行中判定変数を設定
            gblnThread1Exec = True
            '非同期処理①のデリゲートインスタンスを生成
            gobjThread1Delegate = New Thread1Delegate( _
                                   AddressOf fncThread1)
            '非同期処理①の非同期実行を開始
            gobjThread1Return = _
                        gobjThread1Delegate.BeginInvoke( _
                        New AsyncCallback( _
                        AddressOf subThread1Callback), _
                        Nothing)
            blnReturn = True

        Catch ex As Exception
            MsgBox(ex.Message, MsgBoxStyle.Exclamation)
            '非同期処理①実行中判定変数を設定
            gblnThread1Exec = False
            blnReturn = False

        End Try

        Return blnReturn

    End Function

    Public Function fncThread2Start() As Boolean
        '*************************
        '非同期処理②開始処理
        '*************************

        Dim blnReturn As Boolean

        Try
            '非同期処理②実行中判定変数を設定
            gblnThread2Exec = True
            '非同期処理②のデリゲートインスタンスを生成
            gobjThread2Delegate = New Thread2Delegate( _
                                   AddressOf fncThread2)
            '非同期処理②の非同期実行を開始
            '...BeginInvoke(1, 2, の"1, 2,"は"fncThread2"の
            '                  引数(pParam1、pParam2)に該当
            '...subThread2Callback), 2 の"2"は
            '              "subThread2Callback"の引数に該当
            gobjThread2Return = _
                        gobjThread2Delegate.BeginInvoke( _
                        1, 2, _
                        New AsyncCallback( _
                        AddressOf subThread2Callback), 2)
            blnReturn = True

        Catch ex As Exception
            MsgBox(ex.Message, MsgBoxStyle.Exclamation)
            '非同期処理②実行中判定変数を設定
            gblnThread2Exec = False
            blnReturn = False

        End Try

        Return blnReturn

    End Function

    Public Function fncThread1() As THREAD_RETURN
        '*************************
        '非同期処理①
        '引数を必要としない非同期処理
        '*************************

        Dim tReturn As THREAD_RETURN

        Try
            '非同期処理①の戻り値に開始時刻を設定
            tReturn.dStart = Now
            'ここに非同期で実行する処理を記述...(例えば)
            Console.WriteLine("Thread1 started.")
            System.Threading.Thread.Sleep(5000)
            Console.WriteLine("Thread1 ended.")
            '非同期処理①の戻り値に終了時刻、処理結果を設定
            With tReturn
                .dEnd = Now
                .blnState = True
            End With

        Catch ex As Exception
            'エラーが発生した場合
            MsgBox(ex.Message, MsgBoxStyle.Exclamation)
            With tReturn
                .dEnd = Now
                .blnState = False
            End With

        End Try

        Return tReturn

    End Function

    Public Function fncThread2(ByVal pParam1 As Integer, _
               ByVal pParam2 As Integer) As THREAD_RETURN
        '*************************
        '非同期処理②
        '引数を必要とする非同期処理
        '*************************

        Dim tReturn As THREAD_RETURN

        Try
            '非同期処理②の戻り値に開始時刻を設定
            tReturn.dStart = Now
            'ここに非同期で実行する処理を記述...(例えば)
            Console.WriteLine("Thread2 started.")
            Console.WriteLine("Thread2 Parameter1 is " & _
                              pParam1 & ".")
            Console.WriteLine("Thread2 Parameter2 is " & _
                              pParam2 & ".")
            System.Threading.Thread.Sleep(7000)
            Console.WriteLine("Thread2 ended.")
            '非同期処理②の戻り値に終了時刻、処理結果を設定
            With tReturn
                .dEnd = Now
                .blnState = True
            End With

        Catch ex As Exception
            'エラーが発生した場合
            MsgBox(ex.Message, MsgBoxStyle.Exclamation)
            With tReturn
                .dEnd = Now
                .blnState = False
            End With

        End Try

        Return tReturn

    End Function

    Public Sub subThread1Callback(ByVal ar As IAsyncResult)
        '*************************
        '非同期処理①のコールバック関数
        '
        '   ar  :   非同期処理情報インターフェース
        '
        '*************************

        Try
            Do
                If gobjThread1Return.IsCompleted = True Then
                    '非同期処理①が完了した場合
                    Dim tReturn As THREAD_RETURN = _
                            gobjThread1Delegate.EndInvoke( _
                            gobjThread1Return)
                    If tReturn.blnState = False Then
                        '異常終了メッセージを表示
                        MsgBox( _
                        "非同期処理①で異常を検出しました。", _
                        MsgBoxStyle.Exclamation)
                    End If
                    '非同期処理①のデリゲートインスタンスを破棄
                    gobjThread1Delegate = Nothing
                    '非同期処理①の戻り値インスタンスを破棄
                    gobjThread1Return = Nothing
                    '非同期処理①実行中判定変数を設定
                    gblnThread1Exec = False
                    Exit Do
                Else
                    '非同期処理①が実行中の場合
                    System.Threading.Thread.Sleep(250)
                End If
            Loop

        Catch ex As Exception
            MsgBox(ex.Message, MsgBoxStyle.Exclamation)

        End Try

    End Sub

    Public Sub subThread2Callback(ByVal ar As IAsyncResult)
        '*************************
        '非同期処理②のコールバック関数
        '
        '   ar  :   非同期処理情報インターフェース
        '
        '*************************

        'ここでは利用しないけど、引数の取得方法を記述
        Dim intParam As Integer = CType(ar.AsyncState, _
                                        Integer)

        Try
            Do
                If gobjThread2Return.IsCompleted = True _
                   Then
                    '非同期処理②が完了した場合
                    Dim tReturn As THREAD_RETURN = _
                          gobjThread2Delegate.EndInvoke(_
                          gobjThread2Return)
                    If tReturn.blnState = False Then
                        '異常終了メッセージを表示
                        MsgBox( _
                        "非同期処理②で異常を検出しました。", _
                        MsgBoxStyle.Exclamation)
                    End If
                    '非同期処理②のデリゲートインスタンスを破棄
                    gobjThread2Delegate = Nothing
                    '非同期処理②の戻り値インスタンスを破棄
                    gobjThread2Return = Nothing
                    '非同期処理②実行中判定変数を設定
                    gblnThread2Exec = False
                    Exit Do
                Else
                    '非同期処理②が実行中の場合
                    System.Threading.Thread.Sleep(250)
                End If
            Loop

        Catch ex As Exception
            MsgBox(ex.Message, MsgBoxStyle.Exclamation)

        End Try

    End Sub

End Module

------------------------------------------------------------


タグ:VB.NET
nice!(0)  コメント(0)  トラックバック(0) 

nice! 0

コメント 0

コメントを書く

お名前:
URL:
コメント:
画像認証:
下の画像に表示されている文字を入力してください。

トラックバック 0

この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。