Accessの「シングルスレッド」という呪縛を解く:TimerIntervalによる擬似マルチスレッドの実装
Access VBAは、その設計思想からしてシングルスレッドの檻の中にいる。UIスレッドが「計算中」になれば、OSから見ればそれは「応答なし」のフリーズ状態であり、ユーザーの忍耐を試す拷問と化す。
多くの開発者は、`DoEvents`を連発して画面を無理やり更新させるという「泥臭い延命措置」に逃げる。だが、真のエンジニアは違う。私はここで、`Form.Timer`をステートマシン(状態遷移機械)として活用し、重い処理を非同期的に「時分割」で消化する、Accessにおける擬似マルチスレッドの極致を伝授する。
—
1. なぜTimerイベントなのか
`Application.OnTime`のような洗練されたタイマーを持たないVBAにおいて、フォームの`TimerInterval`は唯一の「制御された割り込み」である。
バックグラウンド処理において重要なのは、「一括で処理を完結させないこと」だ。10万件のレコードを一度にループで回せば、Accessは死ぬ。しかし、100件ずつ処理し、残りの時間はOSに制御を返す(Idleにする)という設計にすれば、システムは常に軽快に動き続ける。
2. 実装の要:ステートマシンによるタスク分割
バックグラウンド処理を行うための基盤となる「非同期実行エンジン」のコードを示す。
‘ 擬似マルチスレッド・コントローラー
Option Compare Database
Option Explicit
Private m_TaskState As Long ‘ 現在のタスクの状態を保持するポインタ
Private Const TASK_IDLE As Long = 0
Private Const TASK_RUNNING As Long = 1
‘ メイン処理のエントリポイント
Public Sub StartHeavyProcess()
‘ タイマーを開始(100msごとにTickを発生させる)
Me.TimerInterval = 100
m_TaskState = TASK_RUNNING
End Sub
Private Sub Form_Timer()
If m_TaskState = TASK_IDLE Then Exit Sub
‘ ここで処理を中断・再開可能にする
‘ 大規模ループを関数化し、引数で進捗を管理する
If Not ExecuteChunkedProcess() Then
‘ 処理完了
Me.TimerInterval = 0
m_TaskState = TASK_IDLE
MsgBox “処理が完了しました。”, vbInformation
End If
End Sub
Private Function ExecuteChunkedProcess() As Boolean
‘ ここにDAO/ADOのRecordsetを静的に保持し、
‘ 100件ずつ処理してカーソルを動かすロジックを書く
‘ 完了したらTrue、継続ならFalseを返す
End Function
3. メモリ解放の鉄則:極限の最適化
VBAにおいて「メモリリークは仕様」と開き直る者は三流だ。特に長時間実行されるバックグラウンド処理では、オブジェクトの生成と破棄が積み重なり、Accessのプライベートワーキングセットを肥大化させる。
以下の鉄則を守れ。
- 明示的なNothingの代入: `Set rs = Nothing` は儀式ではない。`CurrentDb` をプロパティとして使い回すのではなく、必要な時に生成し、スコープを抜ける前に必ず解放せよ。
- イベントの無効化: 処理終了時には `TimerInterval = 0` を確実に設定すること。さもなくば、ゾンビのようなイベントがメモリに居座り続ける。
- Windows APIによる監視: 処理が重い場合、`Sleep`関数でCPU負荷を調整するのではなく、`PeekMessage` APIでユーザーの入力(キーボード・マウス)を横取りし、処理を一時中断させるのがプロの作法だ。
‘ CPUを占有しすぎないためのAPI使用例
Private Declare PtrSafe Sub Sleep Lib “kernel32” (ByVal dwMilliseconds As Long)
‘ ※Sleepは多用厳禁。DoEventsと組み合わせ、OSのメッセージキューをクリアする
DoEvents
Sleep 10 ‘ CPU負荷を1%未満に抑えるための微小な待機
4. シニアエンジニアへの忠告:エラーハンドリングの徹底
非同期処理の最大のリスクは、エラーが発生した際の「状態の不整合」にある。メインスレッドで実行されていれば`Err.Raise`はユーザーに直接届くが、バックグラウンドではそうはいかない。
必ず、`On Error GoTo` でエラーを捕捉し、ログファイル(または専用のログテーブル)へスタックトレースを書き出す仕組みを実装せよ。特に、非同期中にフォームが閉じられた場合の `Access.Application` の挙動には注意が必要だ。`OnClose` イベントでタイマーを確実にKillするコードを忘れた者は、システム保守の地獄を見ることになる。
総括
Access VBAで「マルチスレッドっぽいこと」をするのは、決して裏技ではない。それは「限られたリソースの中で、いかにユーザー体験を損なわないか」という、システムアーキテクトとしての矜持である。
泥臭いレガシー環境だからこそ、洗練されたロジックが光る。君たちが明日実装するそのコードが、ユーザーの「フリーズするAccess」というストレスを解消する一つの解となることを期待している。
健闘を祈る。
