【実務・中級編】VB.NETにおけるDispatcherとInvoke/BeginInvokeの仕組み:Windows FormsからWPF/WinUIへの移行期におけるスレッドアフィニティ問題の解決 – Visual Basic (VB / VB.NET)解析バイブル

スポンサーリンク

UIスレッドの「呪い」を解く:VB.NETにおけるスレッドアフィニティとDispatcherの極意

業務自動化ツールを開発していると、必ずぶち当たる壁がある。重いCSV処理やDBクエリを走らせた途端、UIがフリーズし、Windowsから「応答なし」という冷酷な宣告を受ける瞬間だ。

解決策として「別スレッドで処理を回す」のは定石だが、そこで多くのエンジニアが「クロススレッド操作の例外(Cross-thread operation not valid)」という地雷を踏む。

今日は、VB.NETにおいてUIスレッドの排他ルールを完璧に掌握し、安定した非同期処理を実装するための「アーキテクトの作法」を伝授する。

—

1. なぜ「UIスレッド」は神聖不可侵なのか

Windows Forms、WPF、WinUI……どのUIフレームワークを選ぼうと、UIコントロールは「作成したスレッド(メインUIスレッド)」以外からのアクセスを拒絶する。これがスレッドアフィニティだ。

なぜか? 理由は単純だ。UIの描画プロセスやメッセージキューがマルチスレッドの衝突を前提に設計されていないからだ。別スレッドから安易に `Label.Text` を書き換えれば、メモリ破壊や描画の不整合(=バグ)は確定する。

我々エンジニアがやるべきことは、「別スレッドで重い処理をしつつ、結果の反映だけを安全にUIスレッドへ委譲する」という交通整理である。

—

2. DispatcherとInvoke:正しい「橋渡し」の技術

WinFormsでは `Control.Invoke`、WPFでは `Dispatcher.Invoke` を使う。概念は同じだが、実務では「同期か、非同期か」の選択が命運を分ける。

  • Invoke (同期): UIスレッドが空くまで処理を待機させる。多用するとUIがカクつく。
  • BeginInvoke (非同期): 処理をキューに放り込んで即座に処理を継続する。基本はこっちを使え。

【実装サンプル】WPF/WinUIを想定した安全な非同期更新パターン

保守性を考慮し、拡張メソッド化して呼び出しをスマートに隠蔽するのがプロの流儀だ。

Imports System.Windows

Public Module UIThreadExtensions
”’

”’ UIスレッド上で安全にアクションを実行するための拡張メソッド
”’


Public Sub RunOnUI(obj As DependencyObject, action As Action)
‘ Dispatcherが存在するか確認し、なければ即時実行
If obj.Dispatcher.CheckAccess() Then
action.Invoke()
Else
‘ 非同期(BeginInvoke)でUIスレッドへ委譲
obj.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Normal, action)
End If
End Sub
End Module

‘ — 利用側コード —
Public Sub PerformLongRunningTask()
Task.Run(Sub()
‘ ここはワーカースレッド
Dim result = HeavyDatabaseQuery()

‘ UIへ反映する際は拡張メソッドを呼ぶだけ
Me.RunOnUI(Sub()
MyLabel.Content = “処理完了: ” & result
End Sub)
End Sub)
End Sub

—

3. デッドロックを回避する「黄金律」

Invokeの誤用で最も多いのが「UIスレッドがワーカースレッドの終了を待ち、ワーカースレッドがUIスレッドの空きを待つ」というデッドロックだ。

これを防ぐための鉄則は以下の3点だ。

1. UIスレッドで `Task.Wait()` や `.Result` を絶対に使わない: これをやった瞬間にUIスレッドは停止し、非同期処理からのコールバックを受け取れなくなる。必ず `Await` を使え。
2. UIスレッドとの同期ポイントを最小化する: ワーカースレッド内でUIコントロールをこねくり回すな。計算結果のみを渡し、UIの変更は一箇所に集約せよ。
3. 例外処理を閉じ込める: 非同期スレッド内で発生した例外は、UIスレッドまで伝播せずアプリを黙って終了させることがある。必ず `Try…Catch` で囲い、エラーログをUIへ通知するルートを用意しておけ。

—

4. 業務自動化エンジニアへの提言

ファイル操作やDB連携を行うツールにおいて、UIの応答性を保つことは「ユーザー体験」ではなく「信頼性」の問題だ。

  • ファイルI/O: `System.IO` は非同期版(`ReadAsync` 等)を積極的に使え。
  • DBアクセス: `Dapper` や `Entity Framework` を使う場合も、必ず非同期メソッドを選択し、UIスレッドを解放することを意識せよ。

VB.NETは古臭い言語だと言う者がいるが、それは使い手の怠慢だ。Dispatcherの仕組みを理解し、非同期プログラミングを制御下においた時、VB.NETは巨大な業務システムを支える最強の武器へと変貌する。

次回の開発では、安易な `Invoke` で満足せず、「スレッドを跨ぐコスト」を設計の段階で計算してみてほしい。それこそが、伝説のエンジニアへの第一歩だ。

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