【テクニカル・上級編】Windows Formsの「応答なし」を撲滅するIProgressとValueTaskの活用:モダンスレッド間通信の完全実装 – Visual Basic (VB / VB.NET)解析バイブル

スポンサーリンク

Windows Formsの「応答なし」を撲滅せよ:IProgressとValueTaskで実現する非同期UIの極致

長年、VB6の時代からWindows Forms、そして.NETの世界を渡り歩いてきた諸君らなら、一度はあの忌々しい「応答なし(Not Responding)」という文字に冷や汗をかいた経験があるだろう。

UIスレッドを数秒間ロックするだけで、Windowsはユーザーに見放される。`BackgroundWorker`を使って解決したつもりになっている諸君、あるいは力技で `Control.Invoke` を連発し、メモリリークの温床を自ら構築している諸君。今日でその悪癖とは決別だ。

今回は、モダンな非同期プログラミングの核心である `IProgress` と `ValueTask` を用いて、堅牢かつ「滑らか」なUIを実現するためのアーキテクチャを伝授する。

—

1. なぜBackgroundWorkerは「過去の遺物」なのか

`BackgroundWorker` は確かに直感的だった。しかし、イベントベースの非同期パターン(EAP)は、複雑な処理になればなるほど「コールバック地獄」を引き起こす。また、コンテキストスイッチのオーバーヘッドや、例外処理の伝播の難しさは、大規模開発において致命的な負債となる。

我々が求めるのは、「宣言的」でありながら「制御可能」な非同期処理だ。

—

2. IProgress によるスレッドセーフな進捗報告

UIスレッドへのアクセスを安全に行うための定石は、`IProgress` を使うことだ。これを使うと、`Invoke` を意識することなく、UIスレッドへ進捗を「流し込む」ことができる。

実装の極意

‘ UIスレッドから呼び出す非同期メソッドの例
Private Async Sub btnProcess_Click(sender As Object, e As EventArgs) Handles btnProcess.Click
‘ 進捗更新用のプログレスオブジェクトを作成
‘ SynchronizationContextが自動でキャプチャされ、UIスレッドへのマーシャリングを隠蔽する
Dim progress = New Progress(Of Integer)(Sub(percent)
ProgressBar1.Value = percent
lblStatus.Text = $”処理中: {percent}%”
End Sub)

btnProcess.Enabled = False

‘ 重い処理を別スレッドへ逃がす
Await Task.Run(Sub() PerformHeavyWork(progress))

btnProcess.Enabled = True
MessageBox.Show(“完了しました”)
End Sub

Private Sub PerformHeavyWork(progress As IProgress(Of Integer))
For i As Integer = 0 To 100
‘ 実際にはここでシステム間連携やDB操作を行う
Threading.Thread.Sleep(50)

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

—

3. ValueTaskの真価:メモリとパフォーマンスの最適化

`.NET Core` 以降、あるいは `ValueTask` が導入された環境では、単純な `Task` よりも `ValueTask` を優先すべきケースがある。

特に、「同期的に完了する可能性が高い処理」や「頻繁に発生する非同期メソッド」において、`Task` オブジェクトのヒープ割り当て(GC負荷)を回避できるのが最大のメリットだ。

パフォーマンスを極める非同期設計

‘ 頻繁に呼ばれるAPI呼び出しやDBチェックにValueTaskを適用する
Public Async Function CheckSystemStatusAsync() As ValueTask(Of Boolean)
‘ キャッシュヒット時などは同期的に即座に値を返すことで、Task生成コストをゼロにする
If _isCacheValid Then
Return True
End If

‘ 非同期が必要な場合のみTaskを生成して待機
Return Await FetchFromRemoteApiAsync()
End Function

—

4. シニアエンジニアが守るべき「3つの鉄則」

どんなに技術が進化しても、アーキテクチャの根幹を成す原則は不変だ。

1. 例外の捕捉を怠るな: `Async Sub` は例外がスレッドを跨いで握りつぶされる危険がある。必ず `Try-Catch` で囲い、メインスレッドへ適切にエラーを伝播させろ。
2. UIコントロールのDisposeを徹底せよ: Windows APIを直接叩くようなラッパーを作った場合、`IDisposable` の実装は必須だ。特にGDI+やハンドルを扱う際は `GC.SuppressFinalize` を忘れるな。
3. CancellationTokenを常に渡せ: 「応答なし」を防ぐ究極の手段は、ユーザーによる「中断」を許容することだ。`IProgress` とセットで `CancellationToken` を設計に組み込め。

—

5. 結び:レガシーとモダンを繋ぐ技術者であれ

君たちが扱っているのは、単なるコードではない。ビジネスを動かす「システム」だ。

VB.NETは、適切に扱えば極めて強力な武器になる。`.NET 8/9` 時代のモダンな構文を、レガシーなWindows Formsという堅牢なフレームワークの上で走らせる。そのギャップを埋めることこそが、我々アーキテクトの腕の見せ所だ。

「とりあえず動く」コードから、「メンテナンスされ、かつ応答性に優れた」コードへ。今日から書き方を変えろ。それがプロフェッショナルの矜持だ。

—
追記:もし諸君のプロジェクトが.NET Framework 4.8以前で停滞しているなら、`ValueTask` を使うために `System.Threading.Tasks.Extensions` パッケージの導入を検討せよ。環境の壁を言い訳にすることほど、技術者として恥ずべきことはない。

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