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

スポンサーリンク

「応答なし」は恥と思え。VB.NETで実現する非同期処理の極北

現場のエンジニア諸君、まだ `BackgroundWorker` を使っているのか?
あるいは、進捗を通知するために強引に `Control.Invoke` を連発し、UIスレッドを死なせているのか?

Windows Formsにおいて「応答なし(Not Responding)」という文字が出るのは、UIスレッドが死んでいる証拠だ。現代の業務アプリケーションにおいて、これは単なるバグではない。「エンジニアとしての設計怠慢」だ。

今日は、VB.NETにおける非同期処理の決定版、`IProgress(Of T)` と `ValueTask` を組み合わせた、堅牢かつモダンなUIスレッド分離の作法を伝授する。

—

1. なぜ「古いやり方」が死を招くのか

`BackgroundWorker` は、確かに便利だった。だが、現代の非同期プログラミング(Async/Await)において、それは「レガシーな足枷」だ。

  • コンテキストの汚染: イベントベースの非同期処理は、処理の流れを分断し、例外処理を複雑にする。
  • リソースの無駄: `Task` はオブジェクトアロケーションを伴うが、高頻度で発生する小さな処理では `ValueTask` を使うべきだ。
  • スレッドセーフの甘え: `Control.Invoke` を安易に使うと、デッドロックの温床になる。

我々が目指すべきは、「UIスレッドを一切ブロックせず、かつ進捗を型安全に伝える」設計である。

—

2. 究極の非同期設計:IProgress と ValueTask の実装

以下は、ファイル処理などの重いバックグラウンドタスクをUIに安全にフィードバックする、プロダクションレベルのコードだ。

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
_cts = New CancellationTokenSource()
btnStart.Enabled = False

‘ プログレスバーの更新を受け取るハンドラを定義
‘ IProgress(Of T)は自動的にキャプチャされたUIコンテキスト上で実行される
Dim progress = New Progress(Of Integer)(Sub(percent)
ProgressBar1.Value = percent
lblStatus.Text = $”処理中: {percent}%”
End Sub)

Try
‘ 非同期メソッドを呼び出し、完了を待機
Await PerformHeavyTaskAsync(progress, _cts.Token)
MessageBox.Show(“処理が完了しました。”)
Catch ex As OperationCanceledException
lblStatus.Text = “キャンセルされました。”
Finally
btnStart.Enabled = True
End Try
End Sub

”’

”’ ValueTaskを使用することで、同期的に終わる可能性のある処理のメモリ効率を最大化
”’

Private Async Function PerformHeavyTaskAsync(progress As IProgress(Of Integer), token As CancellationToken) As ValueTask
‘ await Task.Run でスレッドプールに処理を投げる
Await Task.Run(Sub()
For i As Integer = 1 To 100
‘ キャンセル要求の監視
token.ThrowIfCancellationRequested()

‘ 重い処理のシミュレーション
Thread.Sleep(50)

‘ 進捗報告
progress.Report(i)
Next
End Sub, token)
End Function

Private Sub btnCancel_Click(sender As Object, e As EventArgs) Handles btnCancel.Click
_cts?.Cancel()
End Sub

End Class

—

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

① `IProgress(Of T)` による疎結合

`IProgress` を使うことで、バックグラウンド処理側は「UIがどうやって表示しているか」を一切知る必要がない。ただ値を流すだけだ。これにより、UIの変更がロジックに影響を与えない疎結合な設計が完成する。

② `ValueTask` の戦略的採用

通常、`Task` はヒープ上にオブジェクトを生成する。もしこのメソッドが秒間数千回呼ばれるような設計であれば、GC(ガベージコレクション)が頻発し、逆にパフォーマンスを落とす。`ValueTask` は、処理が即座に完了する場合にメモリ割り当てを回避できるため、高パフォーマンスが求められる箇所での最適解となる。

③ `CancellationToken` による強制終了

「応答なし」を防ぐもう一つの要素は「キャンセル」だ。ユーザーが処理を中断できるようにするのは、UXの鉄則である。`ThrowIfCancellationRequested()` を適切な箇所に配置することで、スレッドを安全に、かつ即座に終了させることができる。

—

4. 現場のリーダーからの警告

最後に一つだけ、忠告しておく。

「データベースやファイルIOの非同期化」は、ただ `Async` をつけるだけでは意味がない。
本質的な速度向上を狙うなら、基盤となるライブラリが `Async` に対応しているかを確認し、`Task.Run` で単にIO待ちをスレッドプールに逃がすだけでなく、非同期API(`ReadAsync` 等)を直接叩くことだ。

VB.NETは古い言語ではない。書く側の書き方が古いだけだ。
このアーキテクチャを理解すれば、君のアプリケーションは「応答なし」という汚名から解放され、プロフェッショナルなツールへと進化する。

さあ、コードを書き換えろ。現場のUIは君の設計を待っている。

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