【テクニカル・上級編】VB.NETにおけるAsync/AwaitとIProgress(Of T)を用いた非同期処理の進捗率プログレスバー実装 – Visual Basic (VB / VB.NET)解析バイブル

スポンサーリンク

非同期処理の「魂」を実装する:VB.NETにおけるAsync/AwaitとIProgress(Of T)の極致

長年、VB6の`DoEvents`による「なんちゃって非同期」の悪夢と戦ってきた諸君へ。あるいは、WPFのUIスレッドをフリーズさせ、タスクマネージャーで強制終了を繰り返してきた者たちへ。

VB.NETにおけるモダンな非同期処理の本質は、単なる「画面を止めない技術」ではない。「UIスレッドの神聖さを守り、バックグラウンドの混沌を秩序立てて統合する技術」である。

今回は、単なる入門記事で終わらせない。メモリ管理の深淵からUI応答性の限界までを考慮した、現場で戦うための実装論を叩き込む。

1. なぜ「今さら」非同期処理なのか

多くのレガシーシステムは、同期処理の呪縛から逃れられていない。ボタンを押せば処理が終わるまでUIがハングアップし、OSは「応答なし」という冷酷な判決を下す。

これを解決する `Async/Await` は単なる糖衣構文ではない。コンパイラが裏でステートマシンを生成し、コールスタックを再構築する高難易度な魔法だ。この魔法を正しく制御するために、我々は `IProgress(Of T)` というインターフェースを召喚する。

2. 進捗率実装のアーキテクチャ設計

プログレスバーの実装において陥りやすい罠は、「進捗報告の過剰」である。UIスレッドへの通知が秒間数百回発生すれば、メッセージポンプは飽和し、逆にシステム全体のパフォーマンスを殺す。

極限の知見: 進捗更新は「レート制限」を意識せよ。

Imports System.Threading
Imports System.Threading.Tasks

”’

”’ 高負荷なバックグラウンド処理を担当するクラス
”’

Public Class Processor
‘ IProgress(Of T)はUIスレッドを自動でキャプチャする(SynchronizationContext)
Public Async Function ExecuteHeavyTask(progress As IProgress(Of Integer), ct As CancellationToken) As Task
Dim totalSteps As Integer = 1000

For i As Integer = 1 To totalSteps
‘ キャンセル要求を定常的にチェックする
ct.ThrowIfCancellationRequested()

‘ 実際の重い処理(ダミー)
Await Task.Delay(10)

‘ 1%刻みで報告する(高頻度すぎる更新はUIを破壊する)
If i Mod 10 = 0 Then
progress.Report(CInt((i / totalSteps) 100))
End If
Next
End Function
End Class

3. UIスレッドの設計とメモリ最適化

VB.NETでGUIを扱う際、忘れてはならないのが「オブジェクトのライフサイクル管理」だ。`CancellationTokenSource` は使い終わったら必ず破棄(Dispose)しなければならない。さもなくば、マネージドヒープの断片化を招き、長期間稼働するシステムにおいて致命的なメモリリークの温床となる。

Public Class MainForm
Private _cts As CancellationTokenSource

Private Async Sub btnStart_Click(sender As Object, e As EventArgs) Handles btnStart.Click
‘ 前回の残骸を掃除
_cts?.Dispose()
_cts = New CancellationTokenSource()

Dim progressHandler = New Progress(Of Integer)(Sub(percent)
‘ ここはUIスレッドで安全に実行される
ProgressBar1.Value = percent
End Sub)

Try
btnStart.Enabled = False
Dim proc As New Processor()
Await proc.ExecuteHeavyTask(progressHandler, _cts.Token)
MessageBox.Show(“処理完了”)
Catch ex As OperationCanceledException
‘ キャンセル時のハンドリング
MessageBox.Show(“ユーザーによりキャンセルされました”)
Finally
btnStart.Enabled = True
End Try
End Sub

Private Sub btnCancel_Click(sender As Object, e As EventArgs) Handles btnCancel.Click
‘ 処理の停止命令
_cts?.Cancel()
End Sub
End Class

4. シニアエンジニアが押さえるべき「極限の知見」

① Windows APIとの協調

もし、この処理の中で非マネージドなDLL(C++等)を呼び出す場合、`Task.Run` を使用して、明示的にスレッドプールへ処理を逃がせ。UIスレッド上で `Declare Function` を呼び出すことは、たとえそれが一瞬であっても「罪」である。

② スレッド境界の意識

`IProgress(Of T)` がなぜUIスレッドに自動で戻ってこれるのか? それは `SynchronizationContext.Current` をコンストラクタでキャプチャしているからだ。もしコンソールアプリやバックグラウンドサービスでこれを使うと、意図した挙動にならない。環境のコンテキストを理解してコーディングせよ。

③ 破棄の重要性

`CancellationTokenSource` は、`IDisposable` を実装している。Using句で囲むことができない非同期のライフサイクルでは、`Finally` ブロックでの明示的な `Dispose()` を徹底すること。レガシー環境の保守において、メモリリークを放置するのは技術者としての敗北だ。

結びに

非同期処理は、システムの「呼吸」を整える技術だ。
VB.NETは古い言語だと言われることもあるが、.NETの強力なランタイムの上で構築される非同期モデルは、現代のどの言語にも引けを取らない洗練さを持っている。

コードを書くとき、画面の向こう側のユーザーが「止まった」と感じない秒数を意識せよ。その微細な配慮の積み重ねこそが、伝説と呼ばれるアーキテクチャの土台となるのだ。

健闘を祈る。

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