【実務・中級編】上級プロフェッショナル向け:VB.NETにおけるAsync/AwaitとIProgress(Of T)を用いた非同期処理の進捗率プログレスバー実装 – Visual Basic (VB / VB.NET)解析バイブル

スポンサーリンク

VB.NET非同期処理の極意:UIを殺さず、進捗を制する。IProgress(Of T)の正しい設計論

業務自動化ツールを開発していると、必ずぶつかる壁がある。「重い処理を実行中、UIがフリーズし、ユーザーが『壊れた』と判断してタスクマネージャーで強制終了させる」という悲劇だ。

VB.NETにおいて、`DoEvents`を連発するような前時代的な設計は即刻捨てるべきだ。今回は、Async/AwaitIProgress(Of T)を組み合わせ、UIスレッドを完全に解放しつつ、型安全かつ堅牢に進捗をフィードバックする「プロフェッショナル・アーキテクチャ」を伝授する。

1. なぜ「力技」ではいけないのか

多くの初心者は、非同期処理の中で直接UIコントロールを触ろうとする。だが、これは「スレッド間操作の例外」を引き起こす地雷だ。かといって、`Invoke`を多用して無理やり同期させれば、コードの可読性は崩壊し、デバッグ不可能なスパゲッティコードが完成する。

我々が目指すべきは、「重い処理(ドメイン層)」と「UI更新(プレゼンテーション層)」の疎結合化である。ここで鍵となるのが`IProgress(Of T)`インターフェースだ。

2. 実装のベストプラクティス:プロダクションコード

以下のコードは、データベースやファイルIOを伴う重い処理を想定した、実務でそのまま使えるテンプレートだ。

Imports System.Threading
Imports System.Threading.Tasks

Public Class MainForm
‘ 非同期処理のキャンセルを制御するトークンソース
Private _cts As CancellationTokenSource

Private Async Sub btnStart_Click(sender As Object, e As EventArgs) Handles btnStart.Click
‘ 二重起動防止
btnStart.Enabled = False
progressBar.Value = 0
_cts = New CancellationTokenSource()

‘ 進捗レポート用のオブジェクトを生成
‘ UIスレッドの同期コンテキストが自動的にキャプチャされる
Dim progress As New Progress(Of Integer)(Sub(percent)
progressBar.Value = percent
lblStatus.Text = $”処理中: {percent}%”
End Sub)

Try
‘ 非同期メソッドの呼び出し
Await Task.Run(Function() HeavyTask(progress, _cts.Token))
MessageBox.Show(“完了しました。”)
Catch ex As OperationCanceledException
lblStatus.Text = “キャンセルされました。”
Catch ex As Exception
MessageBox.Show($”エラー発生: {ex.Message}”)
Finally
btnStart.Enabled = True
_cts.Dispose()
End Try
End Sub

”’

”’ 重い処理本体(別スレッドで実行される)
”’ UIコントロールには直接触れてはいけない
”’

Private Sub HeavyTask(progress As IProgress(Of Integer), ct As CancellationToken)
For i As Integer = 1 To 100
‘ キャンセル要求の確認
ct.ThrowIfCancellationRequested()

‘ ダミーの重い処理
Thread.Sleep(50)

‘ 進捗を報告
progress?.Report(i)
Next
End Sub
End Class

3. この設計が「堅牢」である理由

① スレッド境界の自動解決

`Progress(Of T)`コンストラクタは、作成されたスレッドの`SynchronizationContext`を保持する。つまり、`HeavyTask`(別スレッド)で`progress.Report`を呼ぶだけで、内部で自動的にUIスレッドへマーシャリングが行われる。`Invoke`を自前で書く必要は一切ない。

② キャンセル(CancellationToken)の徹底

業務ツールにおいて、処理の中断は必須機能だ。`CancellationToken`を渡すことで、タスクを安全に終了させる。`Thread.Abort`のような危険な破壊的手段を使わず、タスク側で能動的に終了を判断させるのが、最も安全な終了手順だ。

③ 責務の分離

`HeavyTask`には、`ProgressBar`や`Label`のインスタンスを渡してはならない。渡すのは`IProgress(Of T)`という「進捗を報告するためのインターフェース」のみだ。これにより、このメソッドをコンソールアプリやWebプロジェクトに移植する際も、UI部分を差し替えるだけで再利用が可能になる。

4. プロフェッショナルへのアドバイス:実務上の注意点

  • 例外処理の範囲: `Task.Run`内で発生した例外は、`Await`している箇所で補足される。例外を握りつぶさない設計を徹底すること。
  • メモリリークに注意: 長時間動くツールの場合、進捗更新で過剰にUIオブジェクトを生成・破棄するとGCに負荷がかかる。更新頻度(例えば1%ごとなど)を適切に間引く実装を検討せよ。
  • データベース連携: DB接続を別スレッドで行う場合、コネクションの寿命に注意が必要だ。`Using`ブロックをスコープの最小単位で活用し、接続を「使い回さない」のが鉄則である。

結びに

「とりあえず動く」コードを書くのは誰にでもできる。しかし、「UIが固まらず、例外を適切にハンドリングし、再利用可能なコード」を書けるのは、限られたエンジニアだけだ。

今日からあなたの書くコードを、このアーキテクチャへと刷新してほしい。それが、あなたの開発者としての価値を一段引き上げるはずだ。現場の「なぜか重い」「たまに落ちる」という報告は、これで過去のものとなるだろう。

タイトルとURLをコピーしました