Access VBAで「擬似マルチスレッド」を制御せよ:TimerIntervalが切り拓く非同期処理の極意
Access開発の現場で、一度は絶望した経験がないだろうか。「重いデータ処理を走らせた途端、画面がフリーズし、Windowsから『応答なし』の宣告を受ける」。
Accessはシングルスレッドで動作する宿命にある。しかし、業務自動化のプロフェッショナルであれば、この制約を「言い訳」にしてはならない。今回は、`TimerInterval`と`OnTimer`イベントを駆使し、メインスレッドを殺さずに重い処理を小分けに実行する「擬似マルチスレッド(タスク・スケジューリング)」の設計思想を伝授する。
—
1. なぜ「同期処理」が現場の地雷になるのか
VBAでループ処理を回す際、`DoEvents`を挟む手法は一般的だが、あれはあくまで「OSに制御権を一時的に返す」だけの場当たり的な対処だ。多用すれば処理速度は低下し、ユーザーが誤ってフォームを閉じればアプリは強制終了(クラッシュ)する。
真のアーキテクトは、「状態保持(State Management)」を実装する。処理を「分割可能な単位」に分解し、Timerイベントが定期的に「次はどこまでやったか?」を確認して実行する。この設計こそが、堅牢なバックグラウンド処理の要諦である。
—
2. 堅牢な実装:ステートマシンによるタスク制御
今回の実装では、クラスモジュールのような設計思想をフォームに持ち込む。重要なのは「処理の状態」を保持する変数をフォームモジュールレベルで定義することだ。
実装コード:BackgroundTaskForm
このフォームを「非表示」あるいは「ステータス表示用」として裏で動かすことで、メイン画面を操作不能にすることなく重い処理を完遂させる。
Option Compare Database
Option Explicit
‘ 処理状態を保持するプライベート変数
Private m_TaskStep As Long
Private m_IsRunning As Boolean
‘ フォームロード時にタイマーを停止状態で待機
Private Sub Form_Load()
Me.TimerInterval = 0
m_TaskStep = 0
m_IsRunning = False
End Sub
‘ 外部から処理を開始させるトリガー
Public Sub StartHeavyTask()
If m_IsRunning Then Exit Sub
m_IsRunning = True
m_TaskStep = 1
Me.TimerInterval = 100 ‘ 100ms間隔でポーリング開始
End Sub
‘ タイマーイベントによる擬似マルチスレッド実行
Private Sub Form_Timer()
On Error GoTo Err_Handler
‘ 処理を分割して実行(ステートマシン)
Select Case m_TaskStep
Case 1
‘ ステップ1: データの準備・検証
Debug.Print “Step 1: データの検証中…”
m_TaskStep = 2
Case 2
‘ ステップ2: 重い外部ファイル読み込みやAPI通信
Debug.Print “Step 2: 重い処理を実行中…”
‘ ※ここでDoEventsは不要。Timerがインターバルを管理するため
m_TaskStep = 3
Case 3
‘ ステップ3: DB更新・クリーンアップ
Debug.Print “Step 3: 完了処理中…”
Me.TimerInterval = 0 ‘ 処理終了、タイマー停止
m_IsRunning = False
MsgBox “処理が完了しました。”, vbInformation
End Select
Exit Sub
Err_Handler:
Me.TimerInterval = 0
m_IsRunning = False
MsgBox “エラー発生: ” & Err.Description, vbCritical
End Sub
—
3. 実務で勝つための「3つの鉄則」
コードをコピペするだけでは一流にはなれない。現場でバグを生まないための「境界条件」を意識せよ。
① 「状態」の排他制御を徹底せよ
`m_IsRunning`フラグを必ず用意すること。ユーザーがボタンを連打しても、処理が二重に走り出す(レースコンディション)を物理的に防がなければならない。
② エラーハンドリングの「タイマー停止」
`OnTimer`内でエラーが発生した際、`TimerInterval`を`0`に戻さなければ、エラーダイアログが無限に表示され続ける「地獄のループ」に陥る。必ずエラーハンドラー内に停止処理を記述せよ。
③ データベース連携の注意点
長時間かけてデータを更新する場合、`CurrentDb`を使い回すのではなく、`DAO.Database`オブジェクトを明示的にオープン・クローズし、トランザクションを適切に制御すること。特にネットワークドライブ上のDBでこれを行う際は、コネクションのライフサイクルに細心の注意を払え。
—
結論:Accessの制約を「設計のスパイス」に
Accessは本来、RADツールとしての側面が強い。しかし、今回のような「Timerによるスケジューリング」を導入することで、高度な非同期処理システムへと昇華させることができる。
「重い処理をどうさばくか」という問いは、プログラミングの本質だ。Accessという閉じた環境でも、脳の使い方次第で無限の拡張性が手に入る。さあ、このコードを武器に、あなたの業務アプリケーションをワンランク上のプロダクトへと引き上げてほしい。
エンジニアの諸君、コードに妥協するな。
