前回、デリゲートの機能の一つを浚いましたので、今回はDelegateの二つ目の機能である「非同期処理」について浚ってみる事に。
色々と調べてみると、VBコンパイラはDelegateを宣言すると、自動的にBeginInvokeメソッドとEndInvokeメソッドを生成してくれます。
UIがフリーズしないようにする為の非同期処理は、この二つのメソッドにより可能となる様です。
【非同期呼出しの勘所】
・呼び出し方:BeginInvoke(デリゲートの仮引数 *1、コールバックデリゲート、Object) As IAsyncResult
・BeginInvoke メソッドを使用する事により、デリゲート経由で呼び出すメソッドを別のスレッドで非同期に実行。
・コールバックメソッドの中で EndInvoke メソッドを呼び出し、メモリリークの可能性を回避。
・デリゲート経由でメソッドを呼び出すと、CLRが管理しているスレッドプールに実行を依頼。その後、制御が直ぐに戻ってくるので呼び出したメソッドとは関係無く、(終了を待たずに)プログラムを先に進める事が可能。
・コールバックメソッドはスレッドプール側で実行されるので、UIを直接更新する事は不可。
*1:引数の数はデリゲート宣言の仮引数に基づくので、お試しソースでは一つ。
<注意点>
・メソッドのシグネチャは同一である事が重要。コールバックメソッドも対応するデリゲートの引数の数と型、戻り値の型に合わせる事。
・BeginInvoke のデリゲート呼出しは非同期で行われる(別スレッド)ので、メソッド呼出し後、直ぐに呼び出し元スレッドに制御が戻る。今回の場合、メッセージボックスのタイトルは「Asynchronous Test」「Asynchronous Method」の順で表示される。
・Windowsフォームは、別スレッドからのUI操作は保障されていないので、②の Label1.Text のコメントを外して実行すると、「有効ではないスレッド間の操作: コントロールが作成されたスレッド以外のスレッドからコントロール 'Label1' がアクセスされました。」と例外が発生する。
お試し環境
Windows7 64bit Edition
Visual Basic 2008 AnyCPU対象
/*-------------------------------- お試し結果 ------------------------------*/
・ボタン2を押すと、直ぐに表示
・その約2秒後に表示
/*----------------------------------------------------------------------------*/
/*---------------------------- お試しソース -------------------------------*/
Public Class Form3
Delegate Sub dlgtDispRpt(ByVal msg As String) ' ①非同期 デリゲートの定義
Private Sub DisplayRpt(ByVal msg As String) ' ②非同期 デリゲートで呼び出すメソッドの作成
System.Threading.Thread.Sleep(2000)
' Label1.Text = "Asynchronous Method" & " and " & msg
MsgBox(msg, MsgBoxStyle.Information, "Asynchronous Method")
End Sub
Private Sub dispCallback(ByVal ar As IAsyncResult) ' コールバックメソッド
Dim arDispRpt As dlgtDispRpt
arDispRpt = CType(ar.AsyncState, dlgtDispRpt)
arDispRpt.EndInvoke(ar)
End Sub
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
Dim dispRpt As dlgtDispRpt ' ③非同期 デリゲート型の変数を宣言
Dim ar As IAsyncResult
dispRpt = New dlgtDispRpt(AddressOf DisplayRpt) ' ④非同期 デリゲート型変数にメソッドを登録
ar = dispRpt.BeginInvoke("See actions", New AsyncCallback(AddressOf dispCallback), dispRpt) ' ⑤非同期 デリゲートの呼び出し
MsgBox("See actions", MsgBoxStyle.Information, "Asynchronous Test")
End Sub
End Class
/*----------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------*/
/*============================================================================*/