Visual Basicでは、非同期呼び出しとイベント処理は、言語のバージョンやアプリケーションのタイプに応じて異なる方法で実現されます。従来のWinFormsアプリケーションでは、VB.NETはイベントキュー(event queue)の処理に通常Application.DoEvents()を使用します。このメソッドは、イベントハンドラーが制御を"解放"し、他のメッセージを処理できるようにし、メインスレッドをブロックしません:
While loading Application.DoEvents() 'UIがユーザーのアクションに反応できるようにします End While
.NETでのVB.NETによる非同期タスクの実行には、次のものが使用されます:
BackgroundWorker — 長時間の操作を別のスレッドに移すためのWinFormsコンポーネントで、UIスレッドとの安全な相互作用を提供します。Async/Awaitキーワード — .NET 4.5で登場した便利で最新の非同期システムで、非同期コードを"同期的な"方法で記述できます:Public Async Function LoadDataAsync() As Task Dim result As String = Await GetWebDataAsync() TextBox1.Text = result End Function
違い:
DoEventsは単にUIメッセージキューを処理し、新しいスレッドを作成しません。BackgroundWorkerは作業を別のスレッドに移し、UIスレッドに安全に戻るためのイベントを持っています。Async/Awaitは非同期性を実現し、UIスレッドへの制御の返却をカプセル化します。ファイルをディスクから読み込み、ProgressBarを更新する必要があるときに、
DoEventsを使用することの危険性は何ですか?
答え:
DoEventsは一時的に他のWindowsイベントに制御を渡しますが、スレッドを解放しません。重いループ(例:大きなファイルの読み込みと同時にUIを更新)で使用すると、ユーザーイベントやマウスイベントが処理され、処理の繰り返しが起こる可能性があり、アプリケーションが"フリーズ"することがあります。重い読み込みが想定される場合は、BackgroundWorkerやTaskを使用するべきです。
間違ったアプローチの例:
For i = 1 To 1000000 ProgressBar1.Value = i / 10000 Application.DoEvents() 'メインスレッドは解放されません! Next
事例
DoEventsを使用しました。その結果、インターフェースが"フリーズ"しました:ユーザーが"開く"ボタンをもう一度クリックすると、ファイルの読み込みプロセスが2回目開始され、最初のプロセスと混ざり合い、データが歪む原因となり、アプリケーションがクラッシュしました。事例
BackgroundWorkerを知らなかったため、DoEventsを使用してフォーム上のグラフを更新することに決めました。膨大なデータポイントの更新中にアプリケーションがフリーズし、その後、Windowsメッセージキューのオーバーフローで"クラッシュ"しました。事例
Async/Awaitを使用した非同期呼び出しを利用しましたが、長時間の処理を"ホットな"UIスレッドで実行できないことを理解せず、Await Task.Run(...)が必要でした。結果:インターフェースはクリックに反応せず、プログレスバーは更新されませんでした。なぜなら、長いメソッドがメインスレッドで実行され続けたからです。