Accessの「擬似マルチスレッド」:Timerイベントで実現する非同期処理の深淵
Access VBAは本質的にシングルスレッドだ。UIスレッドが重い処理に捕まれば、Accessは「応答なし」の仮面を被り、ユーザーの操作を完全に遮断する。しかし、現場の要求は非情だ。「画面はサクサク動かしたいが、裏では常にデータ連携や監視を行いたい」。
この矛盾を打破する唯一の解が、`Form.Timer` を活用した擬似マルチスレッドアーキテクチャだ。今回は、単なるタイマー制御を超えた、メモリ管理とプロセスの安定性を担保する極限の設計論を解説する。
—
1. 非同期処理の設計思想:イベント駆動の罠を回避する
Accessの `TimerInterval` は、単なる周期実行ではない。これは「メッセージループの隙間に割り込む非同期実行のフック」だ。しかし、不用意に `DoEvents` を多用すればスタックオーバーフローや再入(Re-entrancy)の問題を招く。
我々が目指すべきは、「処理の細分化とステートマシンによる制御」だ。
実装の要諦
1. 状態管理(State Machine): タイマーが発火するたびに、現在どのフェーズ(待機、取得、加工、反映)にあるかを判断させる。
2. 排他制御: 処理中に次のタイマーが発火しないよう、`TimerInterval = 0` で一時停止させ、完了後に復帰させる。
3. メモリ最適化: `CurrentDb` を毎回呼び出すのはコストが高い。DB参照は変数にキャッシュし、`Nothing` による明示的解放を徹底する。
—
2. 実装コード:堅牢なバックグラウンドワーカー
以下のコードは、UIをブロックせずに外部テーブルの更新チェックを行うための堅牢なテンプレートだ。
‘ フォームモジュールに配置
Option Compare Database
Option Explicit
‘ 処理状態の列挙型
Private Enum ProcessState
Idle = 0
Fetching = 1
Processing = 2
End Enum
Private m_state As ProcessState
Private m_db As DAO.Database
Private Sub Form_Load()
‘ CurrentDbの参照を保持。毎回呼び出すとパフォーマンスが劣化する
Set m_db = CurrentDb
Me.TimerInterval = 5000 ‘ 5秒間隔
End Sub
Private Sub Form_Timer()
‘ 二重起動防止:処理中はタイマーを停止する
Me.TimerInterval = 0
On Error GoTo ErrHandler
Select Case m_state
Case ProcessState.Idle
‘ 処理開始のトリガー
m_state = ProcessState.Fetching
Call PerformDataCheck
Case ProcessState.Fetching
‘ 非同期処理のメインロジックへ
Call ProcessData
m_state = ProcessState.Idle
End Select
ExitHandler:
‘ 処理終了後、タイマーを再開
Me.TimerInterval = 5000
Exit Sub
ErrHandler:
Debug.Print “Error: ” & Err.Number & ” – ” & Err.Description
Resume ExitHandler
End Sub
Private Sub PerformDataCheck()
‘ ここにDAOやAPI連携の処理を記述
‘ 長いループを回す場合はDoEventsを適切に配置する
End Sub
Private Sub Form_Unload(Cancel As Integer)
‘ オブジェクトの明示的解放はVBAの作法
Set m_db = Nothing
End Sub
—
3. レガシー環境を生き抜くための「極限の知見」
① DoEventsの使いどころ
ループ処理をタイマー内に組み込む場合、`DoEvents` は必須だが、乱用は禁物だ。「100回のループごとに1回」といった頻度調整を行うこと。頻繁すぎるとメッセージキューが溢れ、UIのレスポンスが逆に悪化する。
② Windows APIとの連携
もし外部API(REST API等)を呼び出す場合、`WinHTTP` や `MSXML2.XMLHTTP` を使うことになるが、これらも同期実行だとUIが止まる。
解決策は、「外部VBScriptまたはPowerShellを `WScript.Shell.Run` で非同期起動し、出力ファイルをAccessが監視する」という多重構造だ。Access単体で完結させようとせず、OSのプロセス管理に逃がす勇気を持て。
③ メモリリークを防ぐ鉄則
`CurrentDb` を多用すると、内部のキャッシュ管理が肥大化し、長時間の稼働でAccessが不安定になる。
- 常に `Set db = Nothing` を徹底する。
- `Recordset` を開いたら必ず `Close` し、オブジェクトを開放する。
- フォームの `Timer` イベント内で生成するオブジェクトは、モジュールレベル変数として管理し、使い回す設計にする。
—
結びに代えて
Accessは、開発者が「いかにオブジェクトのライフサイクルを制御するか」によって、その挙動を劇的に変える。`TimerInterval` による擬似マルチスレッドは、一見すると泥臭いハックだが、これは限られたリソースで最大限のパフォーマンスを引き出すための職人芸だ。
「技術的に正しい」ことよりも、「現場で止めずに動かし続ける」ことこそが、真のシニアエンジニアの責務である。このコードをベースに、君のシステムの安定性を極限まで高めてほしい。質問があればいつでも来い。我々の戦場は、コードの行間にあるのだから。
