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
------------------------------------------------------------
コメント 0