【テクニカル・上級編】VB.NETのマルチスレッド処理入門:BackgroundWorkerを使ったUIフリーズのない非同期処理 – Visual Basic (VB / VB.NET)解析バイブル

スポンサーリンク

UIの「応答なし」を撲滅せよ:BackgroundWorkerによる非同期処理の深淵

長年、VB6のレガシーシステムから現代の.NETへと移行する現場を渡り歩いてきた者として、一つ断言できることがある。「UIスレッドを殺す者は、システムを死に至らしめる」

重厚なDBクエリ、巨大なCSVのパース、あるいはシステム間連携のAPI通信。これらをメインスレッドで実行し、画面をフリーズさせてはならない。顧客やユーザーが「このシステムは止まった」と感じた瞬間、そのエンジニアの信頼は地に落ちる。

今回は、VB.NETにおける非同期処理の古典にして至高のツール、`BackgroundWorker`を軸に、メモリ管理とスレッドセーフの極意を伝授する。

1. なぜ「今さら」BackgroundWorkerなのか

非同期処理といえば`async/await`が現代の主流だが、レガシーなWindows Forms環境や、枯れたシステム保守の現場では、依然として`BackgroundWorker`が最も堅牢な選択肢になり得る。

理由はシンプルだ。イベント駆動とコンテキスト同期が標準で実装されているからだ。

`BackgroundWorker`は、バックグラウンドでの処理完了後、自動的にUIスレッドへ戻ってくる。`Invoke`や`BeginInvoke`といった煩雑なデリゲート管理から解放されることは、保守性の観点から見れば非常に大きなアドバンテージとなる。

2. 実装:UIフリーズを回避する定石コード

単に動くだけのコードは誰でも書ける。ここでは、メモリ消費を抑え、進捗を正確にフィードバックする構成を示す。

Imports System.ComponentModel

Public Class MainView
‘ 処理の重い作業をバックグラウンドへ追放する
Private WithEvents worker As New BackgroundWorker()

Private Sub Form_Load(sender As Object, e As EventArgs) Handles MyBase.Load
‘ 進捗報告を有効化
worker.WorkerReportsProgress = True
‘ キャンセル要求を許可
worker.WorkerSupportsCancellation = True
End Sub

Private Sub btnStart_Click(sender As Object, e As EventArgs) Handles btnStart.Click
btnStart.Enabled = False
ProgressBar1.Value = 0
‘ 別スレッドでDoWorkを開始
worker.RunWorkerAsync()
End Sub

‘ 【重要】バックグラウンドスレッドで実行される
Private Sub worker_DoWork(sender As Object, e As DoWorkEventArgs) Handles worker.DoWork
For i As Integer = 0 To 100
If worker.CancellationPending Then
e.Cancel = True
Exit For
End If

‘ ここで重い処理を行う(DB通信、ファイルI/Oなど)
System.Threading.Thread.Sleep(50)

‘ 進捗を報告
worker.ReportProgress(i)
Next
End Sub

‘ 【重要】UIスレッドへ戻ってくる
Private Sub worker_ProgressChanged(sender As Object, e As ProgressChangedEventArgs) Handles worker.ProgressChanged
ProgressBar1.Value = e.ProgressPercentage
End Sub

Private Sub worker_RunWorkerCompleted(sender As Object, e As RunWorkerCompletedEventArgs) Handles worker.RunWorkerCompleted
btnStart.Enabled = True
If e.Cancelled Then
MessageBox.Show(“処理がキャンセルされました。”)
Else
MessageBox.Show(“処理が完了しました。”)
End If
End Sub
End Class

3. シニアエンジニアが意識すべき「メモリとAPIの極意」

オブジェクトの明示的解放(Disposeの徹底)

`BackgroundWorker`自体はManagedなオブジェクトだが、内部でDBコネクションやファイルストリームを開く場合、`Using`構文を必ず使用せよ。GC(ガベージコレクション)に頼り切った設計は、長期間稼働する業務システムにおいて必ずメモリリークを招く。

‘ ダメな例:接続を開きっぱなしにする
‘ 良い例:
Using connection As New SqlConnection(connectionString)
connection.Open()
‘ 処理…
End Using ‘ ここで確実にCloseとDisposeが走る

Windows API呼び出し時の注意点

もしバックグラウンド処理の中で `SendMessage` や `PostMessage` などのWin32 APIを直接叩く必要がある場合、必ずスレッドIDを意識せよ。UIに関連するメッセージは、UIスレッドから発行しないと予期せぬクラッシュを招く。`BackgroundWorker`のイベントハンドラ内はあくまで「バックグラウンド」であることを忘れてはならない。

レガシー環境での性能最適化

古いPC環境で動作させる場合、頻繁な`ReportProgress`はUIスレッドのメッセージキューを飽和させる。進捗更新は「1%ごと」ではなく、タイマーを併用するか、ある程度の粒度(例:5%ごと)で間引くのが、システム全体を安定させる賢者の選択だ。

結びに代えて:技術は「道具」に過ぎない

`BackgroundWorker`は、もはや最新の技術ではない。しかし、VB.NETという言語が持つ「現場のニーズに即座に応える」という哲学を最も体現している機能の一つだ。

どんなに高度なAPIを利用しようとも、ユーザーが直感的に「処理中である」と理解でき、かつシステムが安定して稼働すること。それが我々プロフェッショナルに求められる唯一の成果物である。

コードを書くとき、常に問い続けよ。「このスレッドは、ユーザーの邪魔をしていないか?」と。その問いこそが、真の自動化エンジニアへの道標となる。

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